CORS blocked Angular 7 and Spring 5 - java

I'm running a Spring 5 with Spring Security and Angular 7 project and trying to wire the frontend but keep getting this err message. I should note the projects are two different directories on my computer
OS backend > spring
OS frontend > angular
Access to XMLHttpRequest at 'http://localhost:8080/login' from origin 'http://localhost:4200'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the
requested resource.
I've gone through a bunch of threads here on stackoverflow but none would help.
From what i've gathered the issue is my spring security config
SPRING SECURITY FILE
package com.starter_kit.auth;
import com.starter_kit.auth.Auth.CustomizeAuthenticationSuccessHandler;
import com.starter_kit.auth.Company.CompanyRepo;
import com.starter_kit.auth.Users.UserRepo;
import com.starter_kit.auth.Users.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
// code
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserDetailsService userDetailsService = mongoUserDetails();
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/register").permitAll()
.antMatchers("/dashboard/").hasAuthority("ADMIN").anyRequest()
.authenticated().and().csrf().disable().formLogin().successHandler(customizeAuthenticationSuccessHandler)
.loginPage("/login").failureUrl("/login?error=true")
.usernameParameter("email")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("/**"));
configuration.setAllowedMethods(Arrays.asList("GET","POST","DELETE","PUT"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Spring Controller
#RestController
#CrossOrigin(origins = "http://localhost:4200")
#RequestMapping("/")
public class LoginController {
#Autowired
private UserService userService;
#PostMapping(path = "/login", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public UserDetails login(#RequestBody User user) {
return userService.loadUserByUsername(user.getEmail());
}
}
and my ANGULAR TS HTTP CALL
private loginAuth: string = "http://localhost:8080/login";
public headers = new HttpHeaders({ "Access-Control-Allow-Credentials": "true" })
public loginUser(user: any) {
return this.http.post(
this.loginAuth,
user,
{ headers: this.headers }
);
}
any help would be great

Spring provides an out of the box solution to exclude OPTIONS requests from authorization checks
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().authorizeRequests() ...
}
The cors() method will add the Spring-provided CorsFilter to the application context which in turn bypasses the authorization checks for OPTIONS requests.

Related

Spring security Role based HTTP request Authorization

I am getting 403 forbidden on Deleting an item from the inventory as well as creating a new resource in the database and below is my configuration and the controller written by me.
WebSecurityConfiguration Class:
package com.inventoryservice.config;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("test")
.password("test_pass")
.roles("ADMIN")
.and()
.withUser("store")
.password("store_pass")
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.antMatchers(HttpMethod.DELETE, "/items-management").hasRole("ADMIN")
.antMatchers(HttpMethod.POST, "/items-management").hasAnyRole("ADMIN","USER")
.antMatchers(HttpMethod.GET, "/items-management").permitAll()
.anyRequest().authenticated();
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
Inventory controller:
It has all the endpoints being configured with the database records fetching from the services
package com.inventoryservice.controller;
import com.inventoryservice.dto.request.InventoryRequestDto;
import com.inventoryservice.dto.response.InventoryItemDto;
import com.inventoryservice.dto.response.InventoryResponseDto;
import com.inventoryservice.entity.Inventory;
import com.inventoryservice.service.ItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/items-management")
public class InventoryController {
private ItemService itemService;
#Autowired
public InventoryController(ItemService itemService) {
this.itemService = itemService;
}
#GetMapping
public ResponseEntity<InventoryResponseDto> getItems() {
return new ResponseEntity(
InventoryResponseDto.builder()
.lines(itemService.getItems())
.build()
, HttpStatus.OK
);
}
#PostMapping
public ResponseEntity<InventoryResponseDto> create(#RequestBody InventoryRequestDto inventory) {
return new ResponseEntity(
InventoryResponseDto
.builder()
.lines(itemService.create(inventory.getLines()))
.build()
, HttpStatus.CREATED
);
}
#DeleteMapping
public ResponseEntity delete(#RequestBody InventoryItemDto inventoryItemDto) {
itemService.deleteItems(
inventoryItemDto.getItemIds()
);
return ResponseEntity.ok(inventoryItemDto);
}
}
You've encountered this issue because in Spring Security protection against Cross-Site Request Forgery (CSRF) attack, which aims to trick the user to execute certain action in the application where they are authenticated, is enabled by default.
CSRF protection is meant to guard against undesirable mutating actions, therefore your POST-request fails. For more information on CSRF and when it's justifiable to disable CSRF protection, refer to the documentation.
For now, just in order to test your end-points, you can disable CSRF like that:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.antMatchers(HttpMethod.DELETE, "/items-management").hasRole("ADMIN")
.antMatchers(HttpMethod.POST, "/items-management").hasAnyRole("ADMIN","USER")
.antMatchers(HttpMethod.GET, "/items-management").permitAll()
.anyRequest().authenticated()
.csrf().disable(); // <- add this line
}

How to make Spring security to redirect user to the original requested page after successfully authenticated by the CAS server

I have a spring boot RESTFul web application, which uses CAS server for Enterprise Single Sign-On. If a user, who is not logged-in, tries to access a secure page, that user is redirected to the CAS server for authentication. On successful authentication, the user is redirected to the home page of spring boot RESTFul web application - not the secured page, the user tries to access. How can we directly redirect the user to the secure page, which the user wants to access after successful login?
The spring-security-cas-client is used to implement CAS authentication. An AuthenticationSuccessHandler is implemented to set UseReferer true. The spring security config class is as follows:
package com.example.app
import java.util.Arrays;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutFilter;
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private AuthenticationProvider authenticationProvider;
private AuthenticationEntryPoint authenticationEntryPoint;
private SingleSignOutFilter singleSignOutFilter;
private LogoutFilter logoutFilter;
#Autowired
public SecurityConfig(CasAuthenticationProvider casAuthenticationProvider,
AuthenticationEntryPoint
eP,
LogoutFilter lF
, SingleSignOutFilter ssF
) {
this.authenticationProvider = casAuthenticationProvider;
this.authenticationEntryPoint = eP;
this.logoutFilter = lF;
this.singleSignOutFilter = ssF;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**", "/path-1", "/path-2")
.authenticated()
.and()
.authorizeRequests()
.regexMatchers("/")
.permitAll()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.logout().logoutSuccessUrl("/logout")
.and()
.addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
.addFilterBefore(logoutFilter, LogoutFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(Arrays.asList(authenticationProvider));
}
#Bean
public CasAuthenticationFilter casAuthenticationFilter(ServiceProperties sP) throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setServiceProperties(sP);
filter.setAuthenticationManager(authenticationManager());
return filter;
}
#Bean
public AuthenticationSuccessHandler successHandler() {
SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
handler.setUseReferer(true);
return handler;
}
}
Using of SavedRequestAwareAuthenticationSuccessHandler instead of SimpleUrlAuthenticationSuccessHandler might help you achieve the redirection.
#Bean
public AuthenticationSuccessHandler successHandler() {
SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
handler.setTargetUrlParameter("redirectTo");
handler.setDefaultTargetUrl("/");
return handler;
}

Spring boot OAuth2Client, user not authenticated after callback: SecurityContext is empty or contents are anonymous

I'm configuring a Spring Boot application that will authenticate the user using OAuth2 and OpenID Connect. For the implementation, I follow the reference here: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2
After the AuthorizationCode is done, the user is not authenticated. How do I authenticate the user ?
The OpenID server I rely on requires an extra parameter for the authorization endpoint (acr_values={value}).
I'm able to add the parameter using an AuthorizationRequestResolver and call the authorization endpoint, then the server redirects me on my callback but the user is not authenticated. From the trace, the SecurityContext is empty and not save in the httpsession.
I can see in the logs that the POST request to the token endpoint is sent and I get a response 200.
MySecurityConfig
test
package com.uta.security.edc.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import com.uta.security.edc.oauth2.MyAuthorizationRequestResolver;
import com.uta.security.edc.oauth2.MyTokenResponseConverter;
#Configuration
#EnableOAuth2Client
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ClientRegistrationRepository clientRegistrationRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/connect/**", "/test/**", "/assets/**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.loginPage("/connect/login")
.authorizationEndpoint()
.baseUri("/connect/authorization")
.authorizationRequestResolver(this.authorizationRequestResolver())
.and()
.redirectionEndpoint()
.baseUri("/connect/callback")
.and()
.tokenEndpoint()
.accessTokenResponseClient(this.accessTokenResponseClient())
.and()
.userInfoEndpoint()
.and()
.defaultSuccessUrl("/")
.failureUrl("/connect/loginFailure")
.and()
.oauth2Client();
}
#Bean
public MyAuthorizationRequestResolver authorizationRequestResolver()
{
return new MyAuthorizationRequestResolver(this.clientRegistrationRepository);
}
#Bean
public OAuth2RestTemplate oauth2RestTemplate()
{
return new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
}
#Bean
protected OAuth2ProtectedResourceDetails resource() {
ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId("my-connect");
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
List<String> scopes = new ArrayList<String>(1);
scopes.add("uta-poc-edc");
resource.setAccessTokenUri(clientRegistration.getProviderDetails().getTokenUri());
resource.setClientId(clientRegistration.getClientId());
resource.setClientSecret(clientRegistration.getClientSecret());
resource.setScope(scopes);
return resource;
}
#Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient()
{
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
tokenResponseHttpMessageConverter.setTokenResponseConverter(new MyTokenResponseConverter());
RestTemplate restTemplate = this.oauth2RestTemplate();
//RestTemplate restTemplate = new RestTemplate(Arrays.asList(new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
accessTokenResponseClient.setRestOperations(restTemplate);
return accessTokenResponseClient;
}
/*#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository)
{
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}*/
}
and also MyClientApplication
package com.uta.security.edc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextListener;
#SpringBootApplication
public class myClientApplication extends SpringBootServletInitializer
{
public static void main(String[] args)
{
SpringApplication.run(myClientApplication.class, args);
}
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
}
I need the user to be authenticated and its authentication informations saved in its httpsession in order to request Resource servers.
Thank you for your help!

java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed in spring security spring boot [duplicate]

This question already has answers here:
java.lang.IllegalStateException: Cannot (forward | sendRedirect | create session) after response has been committed
(9 answers)
Closed 4 years ago.
This is my security config in my springboot application
package com.logan.cricketbeting.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.logan.cricketbeting.Service.CustomUserDetailsService;
import com.logan.cricketbeting.Service.UserServiceImpl;
//security configuration class for implementing spring security on urls
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
//for handling user success handler
#Autowired
private CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
#Override
//this configuration is for handling user requests
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/orders").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/admin/**").hasAuthority("admin")
.antMatchers("/distributor/**").hasAuthority("distributor")
.antMatchers("/user/**").hasAuthority("user").anyRequest()
.authenticated().and().csrf().disable().formLogin().successHandler(customizeAuthenticationSuccessHandler)
.loginPage("/login").failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login").and().exceptionHandling().accessDeniedPage("/403");
}
//this method allows static resources to be neglected by spring security
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**","/assets/**","/fonts/**","/vendor/**");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//BCryptPasswordEncoder encoder = passwordEncoder();
//auth.inMemoryAuthentication().withUser("logan#yahoo.com").password(encoder.encode("admin")).roles("user");
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
And this is my customsuccess handler
package com.logan.cricketbeting.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
/*
* This is an Authentication Success handler class for handling what happens after the user is suc
* successfully logined in the application
*
*
*
* */
#Component
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
//set our response to OK status
response.setStatus(HttpServletResponse.SC_OK);
//this check granted authorities against the username
for (GrantedAuthority auth : authentication.getAuthorities()) {
//if the granted authority is user then it is allowed
//if its admin it is allowed
if("admin".equals(auth.getAuthority())) {
response.sendRedirect("/admin/home"); //working fine
}
else if("distributor".equals(auth.getAuthority())) {
response.sendRedirect("/distributor/home");//working fine
}
else if ("user".equals(auth.getAuthority())) {
response.sendRedirect("/user/home");//it is not working
}
//else it redirects to the 403 forbidden error
else {
response.sendRedirect("/403");
}
}
}
}
My admin and distributor url is working fine after login but the user one gives an error
java.lang.IllegalStateException: Cannot call sendRedirect() after the
response has been committed
I dont where is the glitch ,any idea how to solve this???
if the user has multiple authorities your code code calls sendRedirect multiple times. Adding a break or return after each sendRedirect should solve the issue.

Cannot send Authorization Bearer Token using Springfox

I'm having trouble understanding why "Authorization: Bearer __" is not being sent in my api using Springfox 2.5.0. I have the following configuration:
private ApiKey apiKey() {
return new ApiKey(
"Authorization", // name: My key - Authorization
"api_key", // keyname: api_key
"header");
}
#Bean
SecurityConfiguration security() {
return new SecurityConfiguration(
null, null, null,
"Docserver2_fwk", // app name
"BEARER", // api key value
ApiKeyVehicle.HEADER, "Authorization", ",");
}
And the curl being sent is:
It seems I am unable to send "Authorization: Bearer Token" in springfox (2.5.0), is this possible?, is it a known problem?
Similar issue: https://github.com/springfox/springfox/issues/1812
PS: OpenAPI 3.0 allows the "bearer" format, example: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#jwt-bearer-sample
Thanks.
A simple workaround is to type Bearer than paste the token after it. You will end up with a text box that contains:
Bearer <token>
I wish there was a more automated way. But for now, it appears as though what goes in the text box simple get's pasted into the value section of a given header entry. I suppose the reason the prefix Bearer does not get injected automatically is because then Swagger would be quite opinionated about which sort of authentication its users used!
#Configuration
#EnableSwagger2
class SwaggerConfig {
#Bean
Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build()
.securitySchemes(securitySchemes())
}
private static ArrayList<? extends SecurityScheme> securitySchemes() {
return [new ApiKey("Bearer", "Authorization", "header")]
}
}
The REST endpoint method:
#GetMapping("/count")
#ApiOperation(value = "Count the number of entities associated with resource name. This operation does not requires any role." , authorizations = [#Authorization(value = "Bearer")])
def count() {
count(service)
}
The curl command before logging in:
curl -X GET "http://localhost:8080/category/count" -H "accept: */*"
Response:
{
"timestamp": "2018-10-29T15:13:02.388+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/category/count"
}
The curl command after logging in:
curl -X GET "http://localhost:8080/category/count" -H "accept: */*" -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9..."
Response:
{
"message": "There are 0 entities",
"count": 0
}
Note: My code is in Groovy, I am sure you can translate if you are using standard Java.
I am using 2.8.0 versions and below swagger configuration works for me. I mentioned in comments, everything was working fine for me for version 2.7.0 but then I upgraded to 2.8.0 and jwt token stopped being sent in request header. I am on Spring Boot version - 1.5.2.RELEASE .
Note that I wanted an UI where JWT token could be manually entered by user is format - Bearer ... and token should go in Authorization request header. Its not automatically integrated with OAuth server.
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurerAdapter {
#SuppressWarnings("unchecked")
#Bean
public Docket swaggerPlugin() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.paths(PathSelectors.any())
.apis(Predicates.or(RequestHandlerSelectors
.basePackage("**controller package 1**"),
RequestHandlerSelectors
.basePackage("**controller package 2**")))
.build().directModelSubstitute(LocalDate.class, String.class)
.genericModelSubstitutes(ResponseEntity.class)
.apiInfo(apiInfo())
.securitySchemes(Lists.newArrayList(apiKey()))
.securityContexts(Arrays.asList(securityContext()));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("**Comment**")
.description("**Comment**")
.termsOfServiceUrl("**Comment**")
.contact("**Comment**")
.license("Apache License Version 2.0")
.licenseUrl("**Comment**").version("0.0.1")
.build();
}
#Bean
public SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder().scopeSeparator(",")
.additionalQueryStringParams(null)
.useBasicAuthenticationWithAccessCodeGrant(false).build();
}
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations(
"classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
private ApiKey apiKey() {
return new ApiKey("apiKey", "Authorization", "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth())
.forPaths(PathSelectors.any()).build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope(
"global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("apiKey",
authorizationScopes));
}
}
Reference - this github issue answer by JotaroJewstar
As of Springfox 3.0.0, there are dedicated HttpAuthenticationScheme configurations for this kind of authorization.
An example of JWT authorization config:
#Bean
public Docket jwtSecuredDocket() {
HttpAuthenticationScheme authenticationScheme = HttpAuthenticationScheme
.JWT_BEARER_BUILDER
.name("JWT Token")
.build();
return new Docket(DocumentationType.OAS_30)
// <...>
.securitySchemes(Collections.singletonList(authenticationScheme));
}
It is much handier solution because you don't have to put the "Bearer" keyword everytime in the Swagger UI's Auth modal - new mechanism fills that for you (that was a drawback of previous solutions).
Important note
According to the message of the commit introducing this feature, the DocumentationType of the docket has to be set to OAS_30 in order to make this working.
I am using latest version of springfox(right now it's 3.0.0), and you can use specific bearer auth with HttpAuthenticationScheme instead of ApiKey:
HttpAuthenticationScheme
.JWT_BEARER_BUILDER
.name("JWT")
.build()
The solution that worked for me with swagger versiĆ³n 2.9.2 is the following:
package com.example.springsocial;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {
#Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.ignoredParameterTypes(AuthenticationPrincipal.class)
.select()
.apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot")))
.paths(PathSelectors.any()).build()
.securitySchemes(Lists.newArrayList(apiKey()))
.securityContexts(Arrays.asList(securityContext()));
}
private ApiKey apiKey() {
return new ApiKey("apiKey", "Authorization", "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth())
.forPaths(PathSelectors.any()).build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope(
"global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("apiKey",
authorizationScopes));
}
}
POM.XML
<!-- SWAGGER -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>2.9.2</version>
</dependency>
SecurityConfig.JAVA
package com.example.springsocial.config;
import com.example.springsocial.security.*;
import com.example.springsocial.security.oauth2.CustomOAuth2UserService;
import com.example.springsocial.security.oauth2.HttpCookieOAuth2AuthorizationRequestRepository;
import com.example.springsocial.security.oauth2.OAuth2AuthenticationFailureHandler;
import com.example.springsocial.security.oauth2.OAuth2AuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
private CustomOAuth2UserService customOAuth2UserService;
#Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
#Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
#Autowired
private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
#Bean
public TokenAuthenticationFilter tokenAuthenticationFilter() {
return new TokenAuthenticationFilter();
}
/*
By default, Spring OAuth2 uses HttpSessionOAuth2AuthorizationRequestRepository to save
the authorization request. But, since our service is stateless, we can't save it in
the session. We'll save the request in a Base64 encoded cookie instead.
*/
#Bean
public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable()
.formLogin()
.disable()
.httpBasic()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/",
"/error",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js",
// swagger
"/swagger-ui.html**", "/swagger-resources/**",
"/v2/api-docs**", "/webjars/**"
)
.permitAll()
.antMatchers("/auth/**", "/oauth2/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.authorizationEndpoint()
.baseUri("/oauth2/authorize")
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.and()
.redirectionEndpoint()
.baseUri("/oauth2/callback/*")
.and()
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
// Add our custom Token based authentication filter
http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/common/**", "/v2/api-docs", "/configuration/ui", "/swagger-resources",
"/configuration/security", "/swagger-ui.html", "/webjars/**");
}
}
I used springfox-swagger2 version-2.9.2 with below configuration and it works fine to pass JWT token via swagger-ui.
private ApiKey apiKey() {
return new ApiKey("apiKey", Authorization, "header");
}
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("com.mycompany.dept.controller"))
.paths(PathSelectors.any())
.build().apiInfo(metaData()).securitySchemes(Lists.newArrayList(apiKey()));
}
And in controller we need to use #ApiOperation (value = "Get dummy by id.", authorizations = { #Authorization(value="apiKey") })
Please refer to https://github.com/springfox/springfox/issues/2194
The below solution worked for me in swagger 2.8.0 version.
Add the below code in your Docket configuration
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.securitySchemes(Collections.singletonList(new ApiKey("JWT", "Authorization", "header")))
.securityContexts(Collections.singletonList(
SecurityContext.builder()
.securityReferences(
Collections.singletonList(SecurityReference.builder()
.reference("JWT")
.scopes(new AuthorizationScope[0])
.build()
)
)
.build())
)
.select()
.apis(RequestHandlerSelectors
.basePackage("com.test.controller"))
.paths(PathSelectors.regex("/.*"))
.build().apiInfo(apiEndPointsInfo());
}
In the text box after clicking Authorize button in the swagger UI, type Bearer "XXXXXXXX(Token)"
Another working solution with springfox 3.0.0.
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.HttpAuthenticationScheme;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
#Configuration
#EnableOpenApi
public class SwaggerConfig {
#Autowired
private ServletContext context;
#Bean
public Docket api() {
HttpAuthenticationScheme authenticationScheme = HttpAuthenticationScheme.JWT_BEARER_BUILDER
.name("Authorization")
.build();
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.ant(context.getContextPath() + "/api/**"))
.build()
.securityContexts(Arrays.asList(securityContext()))
.securitySchemes(Collections.singletonList(authenticationScheme));
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("Authorization", authorizationScopes));
}
}
Solution given by Jorge Santos Neill worked for me.
Only variation is my SecurityConfig.java as mentioned below
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
#EnableWebSecurity
#EnableResourceServer
#EnableOAuth2Sso
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs", "/configuration/**", "/configuration/ui/**", "/swagger-resources/**",
"/configuration/security/**", "/swagger-ui.html", "/webjars/**", "/health", "/csrf");
}
}
facing same issue, just put simple config and working fine now with bearer authentication key.
Detail implementation here: https://github.com/mnguyencntt/springboot-webflux-swagger/blob/addBearer/src/main/java/com/example/swagger/SwaggerConfiguration.java
package com.example.swagger;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import org.springframework.context.annotation.Configuration;
#Configuration
#SecurityScheme(
name = "Bearer Authentication",
type = SecuritySchemeType.HTTP,
bearerFormat = "JWT",
scheme = "bearer"
)
public class SwaggerConfiguration {
}

Categories