Spring Boot Admin 2.0.0: Client cannot Connect to Secured Server - java

Spring Boot Admin works fine without the spring-boot-starter-security dependency. As soon as I include it, no matter how I configure the security (by application.yml or WebSecurityConfigurerAdapter) everything seems beeing weird.
The result I´m aiming for is that (1) the Spring Boot Admin Server is secured via a login (HTTP Basic Auth for example) and (2) the Client can send data to the secured server.
Here's the configuration I expect to work:
Client: application.yml
spring:
boot:
admin:
client:
url:
- "http://localhost:8090"
instance:
metadata:
user.name: ${myApp.security.usernameAdmin}
user.password: ${myApp.security.passwordAdmin}
username: admin
password: adminPwd
Admin-Server: build.gradle
dependencies {
compile "de.codecentric:spring-boot-admin-starter-server:2.0.0"
compile "org.springframework.boot:spring-boot-starter-security"
}
1st try to make things work
Admin-Server: application.yml
server:
port: 8090
spring:
security:
user:
name: admin
password: adminPwd
logging:
level:
org.springframework.security: DEBUG
result: client can't connect
2nd try to make things work
Admin-Server: WebSecurityConfigurerAdapter
#Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic();
}
}
result: client can't connect
3rd try to make things work
Admin-Server: WebSecurityConfigurerAdapter
#Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private static final String ADMIN_ROLE = "ADMIN";
private static final String ADMIN_PASSWORD = "adminPwd";
private static final String ADMIN_USER_NAME = "admin";
private final String adminContextPath;
public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
adminContextPath = adminServerProperties.getContextPath();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
http.authorizeRequests()
.antMatchers(adminContextPath + "/assets/**").permitAll()
.antMatchers(adminContextPath + "/login").permitAll()
.anyRequest().hasRole(ADMIN_ROLE)
.and()
.formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
.logout().logoutUrl(adminContextPath + "/logout").and()
.httpBasic().and()
.csrf().disable();
// #formatter:on
}
#Override
#Autowired
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(ADMIN_USER_NAME)
.password(ADMIN_PASSWORD)
.roles(ADMIN_ROLE);
;
}
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
result: client cannot connect...

Related

Spring Boot secure actuator endpoint with basic auth while securing other endpoints with Oath [duplicate]

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();
}
}

How to login to the client UI through zuul api-gateway

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.

Spring security - implement oauth2 sso

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.

Security OAuth2 Single Sign Off

I have two clients (client1, client2) and an OAuth (authorization, resource).
I want to logout from one of clients and the other will be logout. I have tried this spring-boot-oauth2-single-sign-off-logout but this only logout my client1 and client2 is still logged in!
Then I try to revoke my tokens while I use this code below:
String username = principal.getName();
Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByClientIdAndUserName("client1", username);
accessTokens.forEach(a -> tokenServices.revokeToken(a.getValue()));
This code did not work, even the client1 is still logged in! While I see my redis is empty and there is no token already, but my client1 is still logged in! How that possible?
===========================================================================
Here is my configuration:
Client - application.yml:
server:
port: 8081
servlet:
context-path: /clt1
spring:
application:
name: client1
thymeleaf:
cache: false
security:
oauth2:
client:
client-id: client1
client-secret: secret1
userAuthorizationUri: http://localhost:8000/oa/oauth/authorize
access-token-uri: http://localhost:8000/oa/oauth/token
scope: read, write
#pre-established-redirect-uri: http://localhost:8081/clt1/callback
#registered-redirect-uri: http://localhost:8081/clt1/callback
#use-current-uri: false
resource:
user-info-uri: http://localhost:8000/oa/user
#jwt:
# key-uri: http://localhost:8000/oa/oauth/token_key
logging:
level:
root: info
Client - SecurityConfig:
#Configuration
#EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers().permitAll()
.anyRequest().authenticated()
.and()
.logout().logoutSuccessUrl("http://localhost:8000/oa/revokeClient").permitAll();
}
}
Oauth - application.yml:
server:
port: 8000
servlet:
context-path: /oa
spring:
application:
name: security
redis:
host: 127.0.0.1
port: 6379
thymeleaf:
cache: false
logging:
level:
root: info
Oauth - AuthorizationConfig:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("client1")
.secret(passwordEncoder.encode("secret1"))
.scopes("read", "write")
.redirectUris("http://localhost:8081/clt1/login")
.authorizedGrantTypes("authorization_code", "refresh_token")
.autoApprove(true)
.and()
.withClient("client2")
.secret(passwordEncoder.encode("secret2"))
.scopes("read", "write")
.redirectUris("http://localhost:8082/clt2/login")
.authorizedGrantTypes("authorization_code", "refresh_token")
.autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore);
}
}
Oauth - ResourceConfig:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests().anyRequest().authenticated();
}
}
Oauth - SecurityConfig:
#Configuration
#EnableWebSecurity
#Order(1)//SecurityConfig >> ResourceConfig
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.requestMatchers()
.antMatchers("/loginPage", "/login**", "/registerPage", "/register", "/oauth/authorize", "/revokeClient")
.and()
.authorizeRequests()
.antMatchers("/registerPage", "/register").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin().loginPage("/loginPage").loginProcessingUrl("/login").permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/docs/**", "/fonts/**", "/img/**", "/js/**", "/plugins/**");
}
}
Oauth - Application:
#SpringBootApplication
#Configuration
public class SsoDemoOauthApplication {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
private RedisConnectionFactory connectionFactory;
#Bean
public TokenStore tokenStore() {
return new RedisTokenStore(connectionFactory);
}
public static void main(String[] args) {
SpringApplication.run(SsoDemoOauthApplication.class, args);
}
}
I admit not beeing too clever, but what about putting
.logout().logoutSuccessUrl("http://localhost:8000/oa/logout").permitAll();
instead of
.logout().logoutSuccessUrl("http://localhost:8000/oa/revokeClient").permitAll();
in SecurityConfig of client app? Any drawback?

Spring w/ #EnableOAuth2Sso client tries to access /oauth/token via GET rather than POST

I have a Spring Boot OAuth2 client & a Spring Boot OAuth 2 ResourceServer that's also an Authorization Service. I know what the problem is. I know OAuth 2 tokens are not allowed via get, only Post. However, I'm at a loss on how to fix it. This seems to come automatically w/ #EnableOAuth2Sso built in. In the code below you can see that class is bare bones. I haven't seen in the WebSecurityConfigurerAdapter mentioning a way to deal w/ this.
I'm not going to include the whole POM but I'm using Spring Boot 1.5.10.RELEASE which includes spring-security-oauth2-2.0.14.RELEASE
I've included my classes & properties files w/ client-id & client-secret XXXed out, Client classes & props first:
Client application.properties:
server.port=7293
server.context-path=/ui
server.session.cookie.name=UISESSION
security.basic.enabled=false
security.oauth2.client.client-id=XXXXX
security.oauth2.client.client-secret=XXXXX
security.oauth2.client.access-token-uri=http://localhost:7291/auth/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:7291/auth/oauth/token
security.oauth2.resource.user-info-uri=http://localhost:7291/auth/user/me
spring.thymeleaf.cache=false
Client Security Config:
#Configuration
#EnableOAuth2Sso
public class UISecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated();
}
}
Client Spring Boot app class:
#SpringBootApplication
public class OAuth2ClientApplication {
public static void main(String[] args) {
SpringApplication.run(OAuth2ClientApplication.class, args);
}
#Bean
public RequestContextListener requestContextListener(){
return new RequestContextListener();
}
}
Resource/Authentication Server App classes:
application.properties:
server.port=7291
server.context-path=/auth
security.oauth2.client.client-id=XXXXX
security.oauth2.client.client-secret=XXXXX
security.oauth2.authorization.checkTokenAccess=isAuthenticated()
security.oauth2.authorization.token-key-access=permitAll()
security.basic.enabled=false
Auth Server Configuration class:
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(FilteringServiceAuthServerConfig.class);
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(clientId)
.secret(clientSecret)
.authorizedGrantTypes("authorization_code")
.scopes("user_info")
.autoApprove(true) ;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
and here's my Authorization Server Web Security Config class:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/token")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager)
.inMemoryAuthentication()
.withUser("XXXXX").password("XXXXX").roles("USER", "ADMIN");
}
}
and the Authorization / Resource Server combo Spring Boot Application class:
#SpringBootApplication
#EnableResourceServer
public class FilteringServiceApp extends SpringBootServletInitializer {
private static Logger logger = LoggerFactory.getLogger(FilteringServiceApp.class);
#Value("${matches.file.name}")
private String fileName;
#Autowired
private FilterMatchRepository matchRepo;
#PostConstruct
private void init() {
new MatchInitializer(matchRepo, fileName).init();
}
/**
* Start up the Filter Matching Application
*
* #param args
*/
public static void main(String[] args) {
SpringApplication.run(FilteringServiceApp.class, args);
}
#Bean
public RequestContextListener requestContextListener(){
return new RequestContextListener();
}
}
the error i get when I access the URL this way (after authenticating via client_id & secret_id is:
{
"error": "method_not_allowed",
"error_description": "Request method 'GET' not supported"
}
i used NGrok to see what was happening as I got this error message & you can see that it's clearly accessing /oauth/token via a GET request which is against the spec. here's that output:
HTTP/1.1 302
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Location: http://localhost:7291/auth/oauth/token?client_id=XXXXX&redirect_uri=http://57bfa798.ngrok.io/ui/login&response_type=code&state=BgLGhq
Content-Length: 0
Date: Mon, 26 Feb 2018 09:42:30 GMT
i changed my AuthServerConfig class
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
//... other methods still the same. below fixed the error
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
}

Categories