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
Related
I am trying to login to the client UI through zuul api-gateway. I have two modules
api-gateway running on port 8000(i.e http://localhost:8000) and erp-service running on 8010 port(i.e http://localhost:8010)
Following configuration I have added in api-gateway application.yml
zuul:
sensitive-headers: Cookie,Set-Cookie
ignoredServices: '*'
admin-services:
path: /_v/**
sensitiveHeaders: Cookie,Set-Cookie
serviceId: erp-service
stripPrefix: false
and in erp-service application.yml
server:
port: 8010
servlet:
contextPath: /_v
I am using spring security formLogin and login page feature.
below is my spring security configuration
#Configuration
#EnableWebSecurity
#EnableScheduling
public class SpringSecurtiyConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Autowired
private AlphaUserDetailsService userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/bootstrap/**").antMatchers("/dist/**").antMatchers("/plugins/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/bootstrap/**").permitAll().antMatchers("/dist/**").permitAll()
.antMatchers("/install/role").permitAll().antMatchers("/plugins/**").permitAll().antMatchers("/login").permitAll()
.anyRequest().authenticated().and().csrf().disable()
.formLogin().loginPage("/login").failureUrl("/login?error=true")
.defaultSuccessUrl("/home", true)
.usernameParameter("email").passwordParameter("password")
.and()
.logout().permitAll().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
.invalidateHttpSession(true).deleteCookies("JSESSIONID").and().exceptionHandling()
.accessDeniedPage("/access-denied").accessDeniedHandler(accessDeniedHandler);
http.headers().frameOptions().sameOrigin();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
}
and Controller
#Controller
public class RootController {
#GetMapping(value = {"/", "/login"})
public String getLandingPage() {
if(!SecurityContextHolder.getContext().getAuthentication().getPrincipal().equals(AlphaConstants.ANONYMOUS_USER)) {
return "redirect:/home";
}
return "login";
}
#GetMapping(value = "/home")
public String getHomePage() {
if(!SecurityContextHolder.getContext().getAuthentication().getPrincipal().equals(AlphaConstants.ANONYMOUS_USER)) {
return "home";
}
return "login";
}
}
I am able to load the login page in browser http://localhost:8000/_v/login
after entering credentials and click login the url in browser http://localhost:8000/_v/login
is changing to http://localhost:8010/_v/login which is erp-service url but I want to it should be remain to api-gateway url with home page like http://localhost:8000/_v/home
I had gone through below links but it did not worked for me.
Zuul Routing on Root Path
Spring Cloud: default redirecting from Gateway to UI
https://github.com/spring-cloud/spring-cloud-netflix/issues/2787
No error in development tool console.
please help.
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?
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.
This question already has an answer here:
Spring Security Authentication issue: HTTP 401
(1 answer)
Closed 4 years ago.
I'm trying to implement SSO using Spring OAuth lib for learning purpose.
The AuthenticationServer looks like this:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory() //
.withClient("acme") //
.secret("acmesecret") //
.authorizedGrantTypes("authorization_code", "refresh_token", "password") //
.scopes("openid");
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
}
WebSecurity like this:
#Configuration
#EnableWebSecurity
public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().loginPage("/login").permitAll().and() //
.logout().and() //
.authorizeRequests().anyRequest().hasRole("USER");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}
and RestController:
#RestController
#EnableResourceServer
public class UserController {
#GetMapping("/user/me")
public Principal user(Principal principal) {
return principal;
}
}
The web application is kept minimal:
#SpringBootApplication
#EnableOAuth2Sso
public class WebApplication implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
application.yml:
spring:
thymeleaf:
cache: false
security:
basic:
enabled: false
oauth2:
client:
clientId: acme
clientSecret: acmesecret
accessTokenUri: http://localhost:9999/auth/oauth/token
userAuthorizationUri: http://localhost:9999/auth/oauth/authorize
resource:
userInfoUri: http://localhost:9999/auth/user/me
when I enter the URL localhost:8080 I should be redirect to the login (generated by spring?), but I get a 401 error.
2018-09-29 12:42:28.257 DEBUG 7677 --- [nio-9999-exec-2] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is anonymous); redirecting to authentication entry point
What am I missing?
Thank you!
You need to set web security configuration in the web application.
The reason why you get 401 is even the login url is not allowed to be connected by Spring Security.
So basically you need to add a java config like the following
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
Hope this helps! Happy Coding :)
I'm trying to configure an authorization server with spring-security-oauth2 and jwt.
My main :
#SpringBootApplication
#EnableResourceServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
My Security config :
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("john").password("123").roles("USER").authorities("FOO_READ");
}
}
My auth server config :
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("gateway")
.secret("secret")
.authorizedGrantTypes("refresh_token", "password")
.scopes("GATEWAY")
.autoApprove(true) ;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenEnhancer(jwtTokenEnhancer()).authenticationManager(authenticationManager);
}
}
And a webservice to access the current user :
#RestController
public class UserController {
#GetMapping("/users/me")
public Principal user(Principal principal) {
return principal;
}
}
Now, if I do a post request like this :
gateway:secret#localhost:10101/oauth/token
grant_type : password
username : john
password : 123
I have a response like :
{
"access_token": "...access token...",
"token_type": "bearer",
"refresh_token": "...refresh token...",
"expires_in": 43199,
"scope": "GATEWAY",
"jti": "...jti..."
}
If I use the access token to call my user WS :
GET localhost:10101/users/me
Authorization : Bearer ...access token...
I obtain a null response.
But if I had this lines to my Security config :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/wtf");
}
I then obtain the appropriate response.
Can someone explain me how this is working ? Why my user is not recognized without this additional line ?
Thank you.
Actually it's a problem with spring security.
The solution is in this thread :
https://github.com/spring-projects/spring-security-oauth/issues/980
This annotation should be added for the Adapter to be correctly called :
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
// ...
}
Another solution is to implement ResourceServerConfigurerAdapter instead of WebSecurityConfigurerAdapter.