I am implementing websockets in spring boot and I want to protect my websocket endpoints with OAuth2, but there is a problem when connecting with SockJS. It seems to completely ignore all the OAuth2 security I've setup.
This is how i configure my stomp endpoints in the class that implemtents WebSocketMessageBrokerConfigurer:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/websocket/endpoint")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker(...)
config.setApplicationDestinationPrefixes(...);
config.setUserDestinationPrefix(...);
}
...
}
and here is my OAuth2 setup:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// configure oauth2 filter
http
// start chain for restricting access.
.authorizeRequests()
// for these paths
.mvcMatchers("/websocket/endpoint")
// require user to at least be authenticated (protect with oauth2)
.authenticated()
// for any other requests
.anyRequest()
// allow everything
.permitAll();
}
...
}
with all above up and running, the code below is able to connect to /websocket/endpoint without any additional setup for OAuth2:
function connect() {
var socket = new SockJS('/websocket/endpoint');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/publicChatRoom', function (helloMessage) {
console.log(helloMessage.body);
showChatMessage(JSON.parse(helloMessage.body).messageContent);
});
});
}
Am i missing any configurations or something?
Related
I am trying to set up multiple WebsecurityConfigurerAdapter for my project where the spring boot actuator APIs are secured using basic auth and all other endpoints are authenticated using JWtAuthentication. I am just not able to make it work together, only the config with the lower order works. I am using Spring Boot 2.1.5.RELEASE
Security Config One with JWT Authenticator
#Order(1)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {
"/docs/**",
"/csrf/**",
"/webjars/**",
"/**swagger**/**",
"/swagger-resources",
"/swagger-resources/**",
"/v2/api-docs"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers("/abc/**", "/abc/pdf/**").hasAuthority("ABC")
.antMatchers("/ddd/**").hasAuthority("DDD")
.and()
.csrf().disable()
.oauth2ResourceServer().jwt().jwtAuthenticationConverter(new GrantedAuthoritiesExtractor());
}
}
The basic Auth config with username/password
#Order(2)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
/* #Bean
public UserDetailsService userDetailsService(final PasswordEncoder encoder) {
final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
User
.withUsername("user1")
.password(encoder.encode("password"))
.roles("ADMIN")
.build()
);
return manager;
}
#Bean PasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/**").hasRole("ADMIN")
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("password").authorities("ADMIN");
}
}
I have been trying to make it work for many days but cannot make both of them work together. If i swap the order, only basic auth works and not the JWT Auth Manager.
I have gone through a lot of SOF Questions, like
[https://stackoverflow.com/questions/40743780/spring-boot-security-multiple-websecurityconfigureradapter][1]
[https://stackoverflow.com/questions/52606720/issue-with-having-multiple-websecurityconfigureradapter-in-spring-boot][1]
[https://github.com/spring-projects/spring-security/issues/5593][1]
[https://www.baeldung.com/spring-security-multiple-entry-points][1]
Nothing seems to be working, is this a known issue in Spring?
To use multiple WebsecurityConfigurerAdapter, you need restrict them to specific URL patterns using RequestMatcher.
In your case you can set a higher priority for ActuatorSecurityConfig and limit it only to actuator endpoints:
#Order(-1)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/actuator/**")
.and()
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
my backend app in spring boot and secured with ssl. I used OAuth2 facebook login. Also the frontend app in Angular 7 and secured by ssl. My problem is sending requests Angular to my Spring boot App. All apps is https.
P.S. All works if i add url to webSecurity.ignoring(). and not secure my backend. i think some problem with security and HTTPS requests. THANKS FOR HELP.
BACKEND
SecurityConfig.java
#RestController
#CrossOrigin(origins = "https://192.168.1.106:4400")
#Configuration
#Order(1000)
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserRepo userRepo;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/unauth/**").permitAll()
.antMatchers(HttpMethod.POST, "/unauth/upload").permitAll()
.antMatchers(HttpMethod.POST, "/api/**").authenticated()
.antMatchers(HttpMethod.PUT, "/api/**").authenticated()
.antMatchers(HttpMethod.DELETE, "/api/**").authenticated()
.antMatchers(HttpMethod.GET, "/api/**").authenticated()
.anyRequest().permitAll()
.and().logout().logoutSuccessUrl("/").permitAll();
}
#Override
public void configure(WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers(HttpMethod.GET, "/unauth/**");
webSecurity.ignoring().antMatchers(HttpMethod.POST, "/unauth/**");
}
webSecurity.ignoring().antMatchers(HttpMethod.POST, "/unauth/**");
}
SomeRestController.java
#RestController
#CrossOrigin(origins = "https://192.168.1.106:4400")
#RequestMapping ("/api")
public class ProductService {
#Autowired
private ProductRepo productRepo;
#CrossOrigin(origins = "https://192.168.1.106:4400")
#GetMapping("/products")
public List<Product> getProducts(){
return productRepo.findAll();
}
SpringBootApplication.java
#SpringBootApplication
#EnableOAuth2Sso
#CrossOrigin(origins = {"https://192.168.1.106:4400"}, allowCredentials = "false")
public class MongoTestApplication {
public static void main(String[] args) {
SpringApplication.run(MongoTestApplication.class, args);
}
}
FRONTEND
SomeComponent.html
< button (click)="makeRequest()"> MAKE REQUEST < /button >
SomeComponent.ts
val:any = {};
makeRequest(){
this.http.get("https://localhost:8443/api/products").subscribe(value => {this.val = value; console.log(this.val.key)});
}
ERROR
error in browser
Access to XMLHttpRequest at 'https://localhost:8443/api/brands' from origin 'https://192.168.1.106:4400' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
core.js.pre-build-optimizer.js:15714 ERROR n {headers: t, status: 0, statusText: "Unknown Error", url: "https://localhost:8443/api/brands", ok: false, …}
Edit your main class as below and remove all #CrossOrigin from the controllers.
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#SpringBootApplication
#EnableOAuth2Sso
public class MongoTestApplication {
public static void main(String[] args) {
SpringApplication.run(MongoTestApplication.class, args);
}
#SuppressWarnings("deprecation")
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "PUT", "POST", "DELETE", "OPTIONS");
}
};
}
}
I want to implement central authentication system with spring security and oauth2 sso. In other words, I have a spring boot application that is responsible for authorization and one simple client. My client has rest API. First I get token from the authorization server, then send a request to client API with an authorization header contains bearer token from above request. But this request always gets me server login page.
Here is the implementation of the server and the client:
Server
AuthorizationServerConfig.java
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("SampleClientId")
.secret("{noop}secret")
.authorizedGrantTypes("password")
.scopes("user_info")
.autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
}
ApplicationConfig:
#SpringBootApplication
#EnableResourceServer
public class ApplicationConfig extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
SecurityConfig:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//this is just example
auth.inMemoryAuthentication().withUser("user").password("{noop}1234").roles("user");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/token")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
application.yml:
server:
port: 8900
servlet:
context-path: /auth
Client:
ApplicationConfig:
#SpringBootApplication
public class ApplicationConfig {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
SecurityConfig:
#Configuration
#EnableOAuth2Sso
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated();
}
}
TestController:
#RestController
public class HomeController {
#GetMapping("/")
public String index() {
return "home";
}
#RequestMapping("/admin")
public String admin() {
return "admin";
}
}
application.yml:
server:
port: 9000
servlet:
context-path: /client1
security:
basic:
enabled: false
oauth2:
client:
clientId: SampleClientId
clientSecret: secret
accessTokenUri: http://localhost:8900/auth/oauth/token
userAuthorizationUri: http://localhost:8900/auth/oauth/authorize
resource:
userInfoUri: http://localhost:8900/auth/user/me
First, I send client_id and secret code along side with username, password and grant_type to localhost:8900/auth/oauth/token and get a result like this:
{
"access_token": "603b505f-e701-43d0-b8b8-976a2178f7ea",
"token_type": "bearer",
"expires_in": 43199,
"scope": "user_info"
}
Now, I pickup above token and send a request to localhost:9000/client1/admin
with header contains above token. But it seems the client application ignores the header and shows server login page as result. How can I fix this problem?
#EnableOAuth2Sso is an annotation for using OAuth 2.0 as an end-user authentication mechanism (e.g. "A Login with Google" button). This annotation is wiring your app to redirect to a login page on your authorization server where you would log in and then get redirected back to your app.
If this is your intent, then you'll need to update your Authorization Server to support the authorization_code grant flow instead of the password grant flow.
However, if your client is strictly a REST API, then you are more likely to need to wire the client using #EnableResourceServer instead of #EnableOAuth2Sso. A Resource Server is what takes a token as authorization, via the Authorization HTTP header.
I am using microservices architecture so I have a separate SSO service which handles all the authentication and authorization requests.
I am using spring websockets in other service and I need to secure it using tokens handled by SSO, so I added this configuration for securing websockets.
#Configuration
#EnableResourceServer
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.nullDestMatcher().authenticated()
.simpTypeMatchers(CONNECT).authenticated()
.simpDestMatchers("/ws/**").hasRole("USER")
.simpSubscribeDestMatchers("/ws/**").hasRole("USER")
.anyMessage().denyAll();
}
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
And for websocket config
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/ws/topic");
config.setApplicationDestinationPrefixes("/ws/view");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/socket/").withSockJS();
}
}
And for remote SSO server
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/api/**").permitAll()
.antMatchers("/api/**").access("#oauth2.hasScope('service-name')");
http.csrf().disable();
http.httpBasic().disable();
}
#Bean
#Primary
#RefreshScope
public CachedRemoteTokenService tokenServices() {
final CachedRemoteTokenService remoteTokenServices = new CachedRemoteTokenService();
remoteTokenServices.setCheckTokenEndpointUrl(getCheckTokenEndPointUrl());
remoteTokenServices.setClientId(getClientId());
remoteTokenServices.setClientSecret(getClientSecret());
return remoteTokenServices;
}
I added the token in the client but it throws AccessDeniedException
var headers = {
Authorization: 'Bearer ' + myToken
}
stompClient.send("/ws/view/update/", headers, JSON.stringify(view));
I checked the SSO server logs and I found it didn't call it at all! Is there something missing?
Any help will be appreciated
i have used this tutorial and it works for me. you can flow the steps : Intro to Security and WebSockets
I have created an Authorization service as follows
#SpringBootApplication
#EnableAuthorizationServer
public class AuthorizationApplication {
...
}
With this application.properties.
server.port=9000
security.oauth2.client.client-id=monederobingo
security.oauth2.client.client-secret=monederobingosecret
security.oauth2.client.authorized-grant-types=authorization_code,refresh_token,password,client_credentials
security.oauth2.client.scope=company,client
Then, in a separate spring boot project I have created a Resource Server.
#SpringBootApplication
#EnableResourceServer
public class App {
...
}
With this application.properties.
server.port=9090
spring.application.name=app
security.oauth2.resource.user-info-uri=http://localhost:9000/user
Now, everything works fine if I send a request like this localhost:9090/api with the appropriate token that was retrieved by Authorization Service.
However, I don't want to send this token when sending requests to localhost:9090/login.
For this I have created this class in my Resource server spring boot app.
#Configuration
public class SpringConfig extends WebSecurityConfigurerAdapter {
#Override protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login")
.permitAll()
.antMatchers("/api/**")
.authenticated();
}
}
And now I don't need to send any token to send a request to /login.
However, I'm now geting the following message when sending request to /api with a valid token.
{
"timestamp": 1496027102659,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/api/v1/points_configuration/314"
}
How can configure security for only a few URL patterns in Spring Security OAuth2?
Kindly follow this for more info regarding Spring OAuth security:Secure Spring REST Api with OAuth
In order to implement OAuth Security in Spring boot, you have to create Authorization & Resource server by extending them from AuthorizationServerConfigurerAdapter and ResourceServerConfigurerAdapter respectively.
Authorization Server
#Configuration
#EnableAuthorizationServer
public class AuthorizationApplication extends AuthorizationServerConfigurerAdapter{
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.userDetailsService(userDetailsService)
.authenticationManager(this.authenticationManager).tokenStore(tokenStore()).approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(mongoClientDetailsService);
/*inMemory()
.withClient(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write")
.authorities("ROLE_CLIENT")
.authorizedGrantTypes("password", "refresh_token","client_credentials")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 18000));*/
}
//Do others stuff
}
Resource Server
All the Url that you want to protect using OAuth should be mentioned in this server configuration. It enables a Spring Security filter that authenticates requests using an incoming OAuth2 token. While mostly WebSecurityConfigurerAdapter extended class is used for basic security configuration like adding filters, allowing un-secure url or implementing session policies etc.
#Configuration
#EnableResourceServer
public class App extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**").and().authorizeRequests()
.antMatchers("/api/**").authenticated();
}
//Do others stuff
}