Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource issue - spring-boot

I got stuck in implementing websocket in my application. I am using Spring Boot + angular 8
This is my websocket configuration
Security configuration
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("****");
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/oauth/token/**").permitAll()
.antMatchers("/websocket/**").permitAll()
.antMatchers("/ws/**").authenticated()
.anyRequest().permitAll().and()
.addFilter(new JwtAuthenticationFilter(authenticationManager(),getApplicationContext()))
.addFilter(new JwtAuthorizationFilter(authenticationManager(),getApplicationContext()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.headers().frameOptions().sameOrigin();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*","http://localhost:4200"));
configuration.setAllowCredentials(false);
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTION", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("Content-Type", "Access-Control-Allow-Headers","Origin", "x-requested-with", "Authorization"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Websocket Configuration
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").setAllowedOrigins("*").withSockJS();
}
}
Below is my controller
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class ClientForwardController {
//Below controller is used to forward all my un mapped request to index.html
// Because of this websocket /info request forwarding to index.html
#GetMapping(value = "/**/{path:[^\\.]*}")
public String forward() {
return "forward:/";
}
#MessageMapping("/test")
#SendTo("/topic/testing")
public String test() throws Exception {
Thread.sleep(3000); // simulated delay 3s
return "success";
}
}
Below is my Angular code to connect to websocket.
import * as Stomp from 'stompjs';
import * as SockJS from 'sockjs-client';
export class WebSocketAPI {
webSocketEndPoint: string = 'http://localhost:8080/websocket';
topic: string = "/topic/testing";
stompClient: any;
displayProductComponent: TodaysMarketListComponent;
constructor(displayProductComponent: TodaysMarketListComponent){
this.displayProductComponent = displayProductComponent;
}
_connect() {
console.log("Initialize WebSocket Connection");
let ws = new SockJS(this.webSocketEndPoint);
this.stompClient = Stomp.over(ws);
const _this = this;
_this.stompClient.connect({}, function (frame: any) {
_this.stompClient.subscribe(_this.topic, function (sdkEvent) {
_this.onMessageReceived(sdkEvent);
});
}, this.errorCallBack);
};
}
But i am not able to connect to server.
Kindly suggest to fix this issue.
Error
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ‘http://localhost:8080/websocket/info?t=1585208056284’. (Reason: Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’).
Whoops! Lost connection to http://localhost:8080/websocket
Thanks,
Venkataramana

Related

Always getting redirected from /authorized to /login page using new spring authorization server

I am using new spring authorization server. And everything was working fine with version 0.3.1. But after migration to 1.0.0. When I am trying to login (and I am supposed to be redirected to /authorized?code=) I am always getting redirected back to login. Could somebody please explain me what is going wrong? Maybe config for security should be specified in one place only?
Thank you.
AuthorizationServerConfig
package oauth2.config;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import oauth2.converter.AuthorizationClientConverter;
import oauth2.handler.FederatedIdentityAuthenticationSuccessHandler;
import oauth2.repository.AuthorizationClientRepository;
import oauth2.repository.ClientRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.RequestMatcher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.util.UUID;
#Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private AuthorizationClientConverter authorizationClientConverter;
#Autowired
private FederatedIdentityAuthenticationSuccessHandler federatedIdentityAuthenticationSuccessHandler;
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
http
.securityMatcher(endpointsMatcher)
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
.authorizeHttpRequests(auth -> {
auth.anyRequest().authenticated();
})
.formLogin(Customizer.withDefaults())
.oauth2Login(Customizer.withDefaults())
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.apply(authorizationServerConfigurer);
return http.build();
}
#Bean
public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return context -> {
JwtClaimsSet.Builder claims = context.getClaims();
if (context.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) {
claims.claim("claim", "zabur_claim");
}
};
}
#Bean
public RegisteredClientRepository registeredClientRepository(ClientRepository clientRepository) {
RegisteredClient registeredClient = RegisteredClient.withId("messaging-client")
.clientId("messaging-client")
.clientSecret(passwordEncoder.encode("secret"))
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1:8080/authorized")
.scope("message.read")
.scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
AuthorizationClientRepository registeredClientRepository = new AuthorizationClientRepository(authorizationClientConverter, clientRepository);
registeredClientRepository.save(registeredClient);
return registeredClientRepository;
}
#Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
#Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer("http://localhost:8080")
.build();
}
#Bean
public TokenSettings tokenSettings() {
return TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofMinutes(5))
.build();
}
private RSAKey generateRsa() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// #formatter:off
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// #formatter:on
}
private KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
}
SecurityConfig
package oauth2.config;
import oauth2.handler.FederatedIdentityAuthenticationSuccessHandler;
import oauth2.handler.FormLoginAuthenticationSuccessHandler;
import oauth2.service.impl.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private FederatedIdentityAuthenticationSuccessHandler federatedIdentityAuthenticationSuccessHandler;
#Autowired
private FormLoginAuthenticationSuccessHandler formLoginAuthenticationSuccessHandler;
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.ignoringRequestMatchers("/signUp", "/logout"))
.authorizeHttpRequests(auth -> {
auth.requestMatchers("/logout", "/signUp", "/authorized").permitAll();
auth.anyRequest().authenticated();
})
.formLogin(formLogin -> formLogin.successHandler(formLoginAuthenticationSuccessHandler))
.oauth2Login(oauth2Login -> oauth2Login.successHandler(federatedIdentityAuthenticationSuccessHandler));
return http.build();
}
#Bean
public DaoAuthenticationProvider authenticationProvider(CustomUserDetailsService customUserDetailsService) {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(passwordEncoder);
authenticationProvider.setUserDetailsService(customUserDetailsService);
return authenticationProvider;
}
}
I am trying to login with OAuth2 and expecting to be redirected to the /authorized?code= but after that I am redirected to /login.
Might you are using lot more code to serve your purpose. I integrated OAuth 2 in my Application using Spring security 6.0.0 and Spring Authorization Server 1.0.0. It made me sweating lot. But I got success. For that I shared my Authorization server configuration as it did.
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http
.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
http
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login"))
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE + 1)
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
http
.cors().and().csrf().disable()
.headers().frameOptions().sameOrigin()
.httpStrictTransportSecurity().disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
I think it may help you a little bit or you may get an idea what actually going on in your code.
SecurityFilterChain by default masks any request for authentication, it acts as a safety net, shadowing any permitAll() matcher. This is somehow not clear in the documentation, as per my understanding for the documentation, and needs to be better clarified.
Allowable paths must be declared in a SecurityFilterChain Bean, while authenticated and authorized paths must be kept in another SecurityChain Bean. To differentiate between allowed and authorized paths, use '.securityMaters()', as shown in the example below:
#Bean
#Order(0)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.ignoringRequestMatchers("/signUp", "/logout"))
.securityMatcher("/logout", "/signUp", "/authorized")
.authorizeHttpRequests(auth -> {
auth.anyRequest().permitAll();
});
return http.build();
}
#Bean
#Order(1)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.ignoringRequestMatchers("/signUp", "/logout"))
.authorizeHttpRequests(auth -> {
auth.anyRequest().authenticated();
})
.formLogin(formLogin -> formLogin.successHandler(formLoginAuthenticationSuccessHandler))
.oauth2Login(oauth2Login -> oauth2Login.successHandler(federatedIdentityAuthenticationSuccessHandler));
return http.build();
}
Try it and let me know how it goes with you!

Spring Boot Keycloak Multi Tenant Configuration

I have a Keycloak instance and created two realms and one user for each realm.
Realm1 (Tenant1) -> User 1
Realm2 (Tenant2) -> User 2
And i have my spring boot application.yml (resource server - API) for one specific realm and fixed in my code.
keycloak:
realm: Realm1
auth-server-url: https://localhost:8443/auth
ssl-required: external
resource: app
bearer-only: true
use-resource-role-mappings: true
It's working and validate for Realm1.
but now i can receive requests from user2 (tenant2) and the token will not be valid because the public key (realm1) is not valid for the signed request jwt token (realm2).
What is the best way to allow multi tenancy and dynamically configuration for multi realms?
thanks,
There's a whole chapter on it: 2.1.18: Multi-Tenanacy
Instead of defining the keycloak config in spring application.yaml, keep multiple keycloak.json config files, and use a custom KeycloakConfigResolver:
public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
#Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
if (request.getPath().startsWith("alternative")) { // or some other criteria
InputStream is = getClass().getResourceAsStream("/tenant1-keycloak.json");
return KeycloakDeploymentBuilder.build(is); //TODO: cache result
} else {
InputStream is = getClass().getResourceAsStream("/default-keycloak.json");
return KeycloakDeploymentBuilder.build(is); //TODO: cache result
}
}
}
I'm not sure if this works well with the keycloak-spring-boot-starter, but I think it's enough to just wire your custom KeycloakConfigResolver in the KeycloakWebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new PathBasedKeycloakConfigResolver();
}
[...]
}
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.http.HttpMethod;
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.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
#DependsOn("keycloakConfigResolver")
#KeycloakConfiguration
#EnableGlobalMethodSecurity(jsr250Enabled = true)
#ConditionalOnProperty(name = "keycloak.enabled", havingValue = "true", matchIfMissing = true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider authenticationProvider = new KeycloakAuthenticationProvider();
authenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(authenticationProvider);
}
/**
* Defines the session authentication strategy.
*/
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.cors()
.and()
.authorizeRequests().antMatchers(HttpMethod.OPTIONS)
.permitAll()
.antMatchers("/api-docs/**", "/configuration/ui",
"/swagger-resources/**", "/configuration/**", "/v2/api-docs",
"/swagger-ui.html/**", "/webjars/**", "/swagger-ui/**")
.permitAll()
.anyRequest().authenticated();
}
}
import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.OIDCHttpFacade;
import java.io.InputStream;
import java.util.concurrent.ConcurrentHashMap;
public class PathBasedConfigResolver implements KeycloakConfigResolver {
private final ConcurrentHashMap<String, KeycloakDeployment> cache = new ConcurrentHashMap<>();
#Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
String path = request.getURI();
String realm = "realmName";
if (!cache.containsKey(realm)) {
InputStream is = getClass().getResourceAsStream("/" + realm + "-keycloak.json");
cache.put(realm, KeycloakDeploymentBuilder.build(is));
}
return cache.get(realm);
}
}
import org.keycloak.adapters.KeycloakConfigResolver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.context.annotation.Bean;
#SpringBootApplication()
public class Application {
public static void main(String[] args) {
SpringApplication.run(DIVMasterApplication.class, args);
}
#Bean
#ConditionalOnMissingBean(PathBasedConfigResolver.class)
public KeycloakConfigResolver keycloakConfigResolver() {
return new PathBasedConfigResolver();
}
}

Error during WebSocket handshake: Unexpected response code: 200 In Jhipster application

I am trying to connect to websocket in my existing Jhipster application which is build using Java spring and Angular 8. At the time of creation of the project I didn't select the websocket option and now there is a need to implement the websockets in the project. I took the help from "https://www.jhipster.tech/using-websockets/" and tried to implement the same solution but I am getting the handshake failure message.
I am using STOMP and sockJS for in websockets.
Please let me know what could be the reasons for handshake failure.
Here is the websocket configuration.java file.
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import hr.nic.vc.security.AuthoritiesConstants;
import java.security.Principal;
import org.springframework.http.server.*;
import java.util.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.*;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.*;
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
#Configuration
#EnableWebSocketMessageBroker
public class WebsocketConfiguration implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config)
{
config.enableSimpleBroker("/topic");
//config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry
registry) {
registry.addEndpoint("/websocket/blog").setHandshakeHandler(defaultHandshakeHandler())
.setAllowedOrigins("*").withSockJS();
}
#Bean
public HandshakeInterceptor httpSessionHandshakeInterceptor() {
return new HandshakeInterceptor() {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
attributes.put(IP_ADDRESS, servletRequest.getRemoteAddress());
}
return true;
}
#Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
};
}
private DefaultHandshakeHandler defaultHandshakeHandler() {
return new DefaultHandshakeHandler() {
#Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
Principal principal = request.getPrincipal();
if (principal == null) {
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS));
principal = new AnonymousAuthenticationToken("WebsocketConfiguration", "anonymous", authorities);
}
return principal;
}
};
}
}
And this is the websocket security file.
public class WebsocketSecurityConfiguration extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages)
{
messages.nullDestMatcher().authenticated().simpDestMatchers("/topic/blog")
.authenticated()
// matches any destination that starts with /topic/
// (i.e. cannot send messages directly to /topic/)
// (i.e. can’t subscribe to /topic/messages/* to get messages which is sent to
// /topic/messages-user)
.simpDestMatchers("/topic/**").authenticated();
// message types other than MESSAGE and SUBSCRIBE
//.simpTypeMatchers(SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE).denyAll()
// catch all
//.anyMessage().denyAll();
}
/**
* Disables CSRF for Websockets.
*/
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
Here is the connect method which is called after authentication for connecting the websocket.
connect():void {
console.log("connect called"); //eslint-disable-line
// build absolute path, websocket doesn't fail during deploying with a context path
let url = '/websocket/blog';
url = this.location.prepareExternalUrl(url);
const authToken = this.authServerProvider.getToken();
if (authToken) {
url += '?access_token=' + authToken;
}
const socket = new SockJS(url);
this.stompClient = Stomp.over(socket);
const headers = {};
this.stompClient.connect(headers, () => {
console.log("this is inside stompclient connect connect"); //eslint-disable-line
this.stompClient.subscribe('/topic/blog', data => {
console.log("This inside subscription"); //eslint-disable-line
this.listenerObserver.next(JSON.parse(data.body));
});
});
}
}
Authorization token is passed with the correct value. Hence there is no error in authorization.
Please let me know if anything else is required.
I am stuck at this point for quite a long time now.
Error:
WebSocket connection to 'ws://localhost:8080/websocket/blog/587/mklduqvp/websocket?access_token=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJoYXJkZWVwc2h1a2xhMDhAbmljLmluIiwiYXV0aCI6IlJPTEVfRU1QTE9ZRUUiLCJleHAiOjE2MDUyNDUwNjd9.DJ2HITaVAiaphd2yg3yPAiLrLI4n8MjszjBasC3zOHrC-73mFdltDPEYHihY16VzPv0rh6EYLj84zCBv37TDNA' failed: Error during WebSocket handshake: Unexpected response code: 200
In your DefaultHandshakeHandler, can you try to annotate #Bean and set to public.
#Bean
public DefaultHandshakeHandler defaultHandshakeHandler() {

How I can fix broken CORS In Spring Boot + Vue app?

In my problem I have Spring Boot Application (which is using Spotify API) on backend and Vue application on front. I use server on localhost:8080 and front on localhost:8081. I want to connect my frontend to my backend via axios and I try everything and still get CORS error.
When I call test GET endpoint /getList() I' ve got
Access to XMLHttpRequest at 'http://localhost:8080/getList' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
When I try to call POST /findTracks() I've got:
Access to XMLHttpRequest at 'http://localhost:8080/findTracks' from origin 'http://localhost:8081' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
And I alread tried everything (as you can see in the code below).
First:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#EnableWebMvc
public class CorsConfiguration implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**").allowedHeaders("*").allowedMethods("*");
} //even with .allowedOrgins("http://localhost:8081");
}
Then in Controller class:
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RestController
public class SpotifyApiController {
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RequestMapping(value = "/getList", method = RequestMethod.GET)
public List<String> getList() {
ArrayList<String> a = new ArrayList<>();
a.add("dwa");
a.add("trzy");
return a;
}
#RequestMapping(value = "/findTracks",
method = RequestMethod.POST,
consumes = "application/json",
produces = "application/json")
public List<Track> getTracksForTitles(#RequestBody TrackWrapper userTracks, TrackService tracksService, OAuth2Authentication details) {
return tracksService.generateTracksDetails(getActiveToken(details), userTracks);
}
Then in Vue:
import axios from 'axios';
const SERVER_URL = 'http://localhost:8080'
const instance = axios.create({
baseURL: SERVER_URL,
timeout: 1000
});
export default{
findTracksInSpotify:(jsonObject)=>instance.post('/findTracks',{
userTracks: jsonObject.userTracks,
headers:{
'Content-Type': 'application/json',
}
}).then(() => function(data){
return JSON.parse(data)
}),
getList:()=>instance.get('/getList',{
transformResponse:[function(data){
return JSON.parse(data)
}]
}),
}
And my Spring Security class if needed:
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.context.request.RequestContextListener;
#Configuration
#EnableOAuth2Sso
#EnableWebSecurity
public class OAuth2Configuration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated()
.and().logout().logoutSuccessUrl("/").permitAll();
}
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
}
I even install chrome extension but it does not work either.
Can you tell me what I am doing wrong?
I think that you do not need the class CorsConfiguration.
You do not need to annotate with CrossOrigin the SpotifyApiController either.
The configuration of CORS ideally should be placed in the security configuration. Something like that (in OAuth2Configuration):
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
#Configuration
#EnableOAuth2Sso
#EnableWebSecurity
public class OAuth2Configuration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// The configuration that you needed
// If preflight requests are redirected by OAuth conf, you can try adding:
// .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// CORS configuration
// This value must be parameterized according to your application needs
final String corsOrigin="http://localhost:8081";
// The idea is to insert the CORS filter before the filter injected by
// the #EnableOAuth2Sso annotation
http.addFilterBefore(new CorsFilter(corsConfigurationSource(corsOrigin)), AbstractPreAuthenticatedProcessingFilter.class);
}
private CorsConfigurationSource corsConfigurationSource(String corsOrigin) {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList(corsOrigin));
configuration.setAllowedMethods(Arrays.asList("GET","POST","HEAD","OPTIONS","PUT","PATCH","DELETE"));
configuration.setMaxAge(10L);
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Accept","Access-Control-Request-Method","Access-Control-Request-Headers",
"Accept-Language","Authorization","Content-Type","Request-Name","Request-Surname","Origin","X-Request-AppVersion",
"X-Request-OsVersion", "X-Request-Device", "X-Requested-With"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
There is a sample of RestConfiguration corsfilter. You can add the following bean to your code:
#CrossOrigin
#Configuration
public class RestConfiguration {
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
final FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(request -> {
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedOrigins(
Lists.newArrayList("*"));
cors.setAllowedMethods(Lists.newArrayList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
cors.setAllowedHeaders(Lists.newArrayList("*"));
return cors;
}).and().csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("")
.permitAll().and()
.addFilterBefore(setLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
}
Did you try using #CrossOrigin(origins="http://localhost:8081") on your controller class and repository class?
Also in conjuction to it : Try to add WebConfigurer Bean in you main SpringBoot Application class and annonate that too with #CrossOrigin(origins="http://localhost:8081")
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
System.out.println("here");
registry.addMapping("/**").allowedOrigins("http://localhost:8081").allowedMethods("PUT", "DELETE" )
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);;
}
};
}
Please visit this link too for enabling CORS in your application server side and check as per your configuration which CORS method you can use.

How avoid the certificate validation in spring-boot-admin?

In what way can I avoid certificate validation in spring-boot-admin?
Link error image:
https://ibb.co/fkZu8y
I configure the RestTemplate for avoid the certificate in a class, but I do not know how to send it, I guess it must be in the client, the spring-boot-admin-starter-client works automatically.
This is the code for avoid the certificate validation.
public class SSLUtil {
public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
return true;
}
};
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
}
Application.properties
spring.application.name=Admin-Application
server.port=1111
security.user.name=admin
security.user.password=admin123
#Configuration
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Page with login form is served as /login.html and does a POST on
// /login
http.formLogin().loginPage("/login.html").loginProcessingUrl("/login").permitAll();
// The UI does a POST on /logout on logout
http.logout().logoutUrl("/logout");
// The ui currently doesn't support csrf
http.csrf().disable().authorizeRequests()
// Requests for the login page and the static assets are
// allowed
// http.authorizeRequests()
.antMatchers("/login.html", "/**/*.css", "/img/**", "/third-party/**").permitAll();
// ... and any other request needs to be authorized
http.authorizeRequests().antMatchers("/**").authenticated();
// Enable so that the clients can authenticate via HTTP basic for
// registering
http.httpBasic();
}
}
I'm using Spring Boot Admin 2.1.3 together with Eureka.
It seems SBA has moved from RestTemplate to WebClient. So I create a WebClient which has a SSLContext with a trust manager set to InsecureTrustManagerFactory, that trusts everything. Then I use this webclient and instantiate SBA's InstanceWebClient. Not sure if there is an easier approach, but this worked for me.
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import de.codecentric.boot.admin.server.web.client.HttpHeadersProvider;
import de.codecentric.boot.admin.server.web.client.InstanceExchangeFilterFunction;
import de.codecentric.boot.admin.server.web.client.InstanceWebClient;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.ConnectionObserver;
import reactor.netty.http.client.HttpClient;
import javax.net.ssl.SSLException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
#Configuration
#EnableConfigurationProperties(AdminServerProperties.class)
public class SslConfiguration {
private final AdminServerProperties adminServerProperties;
public SslConfiguration(AdminServerProperties adminServerProperties) {
this.adminServerProperties = adminServerProperties;
}
#Bean
public InstanceWebClient instanceWebClient(HttpHeadersProvider httpHeadersProvider,
ObjectProvider<List<InstanceExchangeFilterFunction>> filtersProvider) throws SSLException {
List<InstanceExchangeFilterFunction> additionalFilters = filtersProvider.getIfAvailable(Collections::emptyList);
return InstanceWebClient.builder()
.defaultRetries(adminServerProperties.getMonitor().getDefaultRetries())
.retries(adminServerProperties.getMonitor().getRetries())
.httpHeadersProvider(httpHeadersProvider)
.webClient(getWebClient())
.filters(filters -> filters.addAll(additionalFilters))
.build();
}
private WebClient getWebClient() throws SSLException {
SslContext sslContext = SslContextBuilder
.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient httpClient = HttpClient.create()
.compress(true)
.secure(t -> t.sslContext(sslContext))
.tcpConfiguration(tcp -> tcp.bootstrap(bootstrap -> bootstrap.option(
ChannelOption.CONNECT_TIMEOUT_MILLIS,
(int) adminServerProperties.getMonitor().getConnectTimeout().toMillis()
)).observe((connection, newState) -> {
if (ConnectionObserver.State.CONNECTED.equals(newState)) {
connection.addHandlerLast(new ReadTimeoutHandler(adminServerProperties.getMonitor().getReadTimeout().toMillis(),
TimeUnit.MILLISECONDS
));
}
}));
ReactorClientHttpConnector reactorClientHttpConnector = new ReactorClientHttpConnector(httpClient);
return WebClient.builder().clientConnector(reactorClientHttpConnector).build();
}
}
To disable the SBA Admin server from validating SSL certs from the clients it tries to connect to, you can use the following:
For SBA version 2.6.2 it is more or less outlined right from their documentation: https://codecentric.github.io/spring-boot-admin/current/#_using_mutual_tls
Here is the complete configuration overriding bean:
package com.markham.mkmappadmin.config;
import javax.net.ssl.SSLException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import reactor.netty.http.client.HttpClient;
/**
* Custom http client class which overrides Spring Boot Admin's server default client.<br>
* The custom client will bypass any SSL Validation by configuring an instance of
* {#link InsecureTrustManagerFactory}
* #author Hanif Rajabali
* #see Spring Boot Admin 2.6.2 Using Mutual TLS
*/
#Configuration
public class CustomHttpClientConfig {
#Bean
public ClientHttpConnector customHttpClient() throws SSLException {
SslContext sslContext = SslContextBuilder
.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient httpClient = HttpClient.create().secure(
ssl -> ssl.sslContext(sslContext)
);
return new ReactorClientHttpConnector(httpClient);
}
}
What I still haven't figured out is how to disable it from the SBA client. I have a custom RestTemplate Config defined below, but the SBA client doesn't seem to be picking it up even though I see that the SBA client code is using the BlockingRegistrationClient i.e) RestTemplate
package com.markham.mkmemailerws.config;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* Need to explicitly build Spring Boot's auto configured
* {#link #restTemplate(RestTemplateBuilder)}
*
* #author Hanif Rajabali
*
*/
#Configuration
public class RestTemplateConfig {
// #Bean
// public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
// return restTemplateBuilder.build();
// }
/**
* The following will bypass ssl validation altogether. Not ideal.
*/
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder)
throws NoSuchAlgorithmException, KeyManagementException {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
} };
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build();
HttpComponentsClientHttpRequestFactory customRequestFactory = new HttpComponentsClientHttpRequestFactory();
customRequestFactory.setHttpClient(httpClient);
return builder.requestFactory(() -> customRequestFactory).build();
}
}
Try http.csrf().disable().authorizeRequests()
Above code will disable csrf token. Below is my code for OAuth where I disabled csrf to reduce complexity.
#RestController
#EnableOAuth2Sso
#EnableResourceServer
#SpringBootApplication
public class SpringBootWebApplication extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/api/**", "/dashboard", "/welcome","/about").authenticated().antMatchers("/**").permitAll()
.anyRequest().authenticated().and().logout().logoutSuccessUrl("/").permitAll();
}

Resources