AuthorizeHttpRequest in Springboot Security - spring

I want the root only to be publicly accessible, this is how I'm doing it, but it doesn't seem to work as every URL including the root requires me to pass by the login interface
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.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
public class ApplicationSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http)throws Exception{
http.authorizeHttpRequests()
.requestMatchers("/")
.permitAll()
.anyRequest().authenticated()
.and().httpBasic();
return http.build();
}
}

.requestMatchers("/**") should fix it.
More details:
#Configuration
#EnableWebSecurity
#EnableMethodSecurity
public class SecurityConfig {
Add the EnableMethodSecurity another to allow some PreAuths on API pathing via your controllers, I like to batch do this via Interfaces as Spring Boot now allows it.
#PostMapping("/URL")
#PreAuthorize("hasRole('ROLE')")
public ResponseEntity<T> method(
The PreAuthorize annotation is basically short way of doing hasRole in your security filter chain.

Related

401 sent to angular frontend by spring boot backend after successful oauth2 authetication with google

Spring Boot rest api in the back and angular in the front.
Hi all, I have a problem after successful oauth2 authentication with google.
In srping boot debug I can read the following:
o.s.web.cors.DefaultCorsProcessor : Skip: response already contains "Access-Control-Allow-Origin".
Then a 401 is sent to angular with full authentication required to access /api/user/ resource which is the root to access user details in the backend side.
WebConfig.java
import java.util.Locale;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
#Configuration
public class WebConfig implements WebMvcConfigurer {
private final long MAX_AGE_SECS = 3600;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("*")
.allowedMethods(
"HEAD",
"OPTIONS",
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
)
.maxAge(MAX_AGE_SECS);
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
final CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setDefaultLocale(Locale.ENGLISH);
return cookieLocaleResolver;
}
#Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
}
SecurityConfig.java
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.client.RestTemplate;
import com.springboot.dashboard.security.jwt.TokenAuthenticationFilter;
import com.springboot.dashboard.security.oauth2.DashBoardOAuth2UserService;
import com.springboot.dashboard.security.oauth2.DashBoardOidcUserService;
import com.springboot.dashboard.security.oauth2.HttpCookieOAuth2AuthorizationRequestRepository;
import com.springboot.dashboard.security.oauth2.OAuth2AccessTokenResponseConverterWithDefaults;
import com.springboot.dashboard.security.oauth2.OAuth2AuthenticationFailureHandler;
import com.springboot.dashboard.security.oauth2.OAuth2AuthenticationSuccessHandler;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private DashBoardOAuth2UserService dashBoardOAuth2UserService;
#Autowired
private DashBoardOidcUserService dashBoardOidcUserService;
#Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
#Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable()
.formLogin()
.disable()
.httpBasic()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/", "/error", "/api/all", "/api/auth/**", "/oauth2/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.authorizationEndpoint()
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.and()
.redirectionEndpoint()
.and()
.userInfoEndpoint()
.oidcUserService(dashBoardOidcUserService)
.userService(dashBoardOAuth2UserService)
.and()
.tokenEndpoint()
.accessTokenResponseClient(authorizationCodeTokenResponseClient())
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
// Add our custom Token based authentication filter
http.addFilterBefore(
tokenAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class
);
}
#Bean
public TokenAuthenticationFilter tokenAuthenticationFilter() {
return new TokenAuthenticationFilter();
}
/*
* By default, Spring OAuth2 uses
* HttpSessionOAuth2AuthorizationRequestRepository to save the authorization
* request. But, since our service is stateless, we can't save it in the
* session. We'll save the request in a Base64 encoded cookie instead.
*/
#Bean
public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
// This bean is load the user specific data when form login is used.
#Override
public UserDetailsService userDetailsService() {
return userDetailsService;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeTokenResponseClient() {
OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
tokenResponseHttpMessageConverter.setTokenResponseConverter(
new OAuth2AccessTokenResponseConverterWithDefaults()
);
RestTemplate restTemplate = new RestTemplate(
Arrays.asList(
new FormHttpMessageConverter(),
tokenResponseHttpMessageConverter
)
);
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
DefaultAuthorizationCodeTokenResponseClient tokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRestOperations(restTemplate);
return tokenResponseClient;
}
}
Thanks in advance for your help.
oauth2 login successful cause user data is successfully to database, but can access to full authentication resource.
WebSecurityConfigurerAdapter is deprecated, don't use it (it is not even there any more in spring-boot 3). Expose a SecurityFilterChain bean instead:
#Bean
SecurityFilterChain filterChain(HttpSecurity http) {
// http configuration
return http.build();
}
Resource-server (REST API)
Instead of writing all of spring-boot-starter-oauth2-resource-server security configuration in java #Configuration (CSRF, CORS, JWT decoder or token introspector, authorities mapping, public routes), you can use one of the spring-boot starters here:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<!-- replace "webmvc" with "weblux" if your app is reactive -->
<!-- replace "jwt" with "introspecting" to use token introspection instead of JWT decoding -->
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<!-- this version is to be used with spring-boot 3.0.0-RC2, use 5.x for spring-boot 2.6.x or before -->
<version>6.0.5</version>
</dependency>
#EnableMethodSecurity
public static class WebSecurityConfig { }
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/master
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,ressource_access.client-id.roles
com.c4-soft.springaddons.security.cors[0].path=/**
Client (Angular app)
Use an OAuth2 client library. My favorite for Angular is angular-auth-oidc-client. It will save you tones of efforts to:
redirect users to authorisation-server for login
handle redirect back from authorization-server with authorization-code
exchange authorization-code for tokens (access-token of course, but also refresh and ID tokens if you requested offline_access and openid scopes)
auto-refresh access tokens before it expires (if you got a refresh-token)
automatically authorize request matching configured patterns (add Authorization Bearer header with access-token)
provide with Angular route guards
...
Authorization-server
Unless you deploy your resource-server to Google cloud, it is likely it can't use Google authorization-server directly. You might use an other authorization-server capable of identity federation in front of it. Keycloak does it pretty well:
run a Keycloak instance and configure it with Google as identity provider
configure your resource-server to use Keycloak as authorization-server (as done in code above)
configure Angular to use Keycloak as authorization-server too
I refer to Keycloak here, but most serious OIDC providers (either on premize or SaaS like Auth0, Okta, etc.) support "social" login and will allow Google users to login (as well as Facebook, Github, Tweeter, etc.)

How single authentication can work for multiple server nodes in Spring Security

I am using weblogic for deploying my spring boot application, and my same application is deployed on multiple nodes.
For example the two node in which the application is deployed is 9001 and 9002.
With basic security even if I am authenticated on the Node 9001 and trying to access the same URL on second node i.e on 9002, I am again getting redirected again to spring login page for authentication.
I want that once I authenticate using username and password on any node. I need not to authenticate again, Even if I am requesting to any other node.
Any kind of clue or help will be appreciated.
Thanks in advance.
The Security configuration file is
package com.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("test")
.password("{noop}test")
.authorities("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/userdetail").authenticated()
.anyRequest().permitAll()
.and()
.formLogin();
}
}
In my case it worked for both node when I enabled RedisHttpSession.
Below is the code which worked for me.
#Configuration
#EnableRedisHttpSession
public class RedisConfig {
#Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
}
also in pom.xml I needed to make two dependencies(For Spring boot).
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
You can also take reference about EnableRedisHttpSession from spring docs, and about spring session from
https://docs.spring.io/spring-session/docs/current/api/org/springframework/session/data/redis/config/annotation/web/http/EnableRedisHttpSession.html
https://www.baeldung.com/spring-session

How to prevent the user after logout when he enter the url in address bar he should get the login page

I have created a web application. Everything works fine.But, if the user is not logged in still they can have access to other jsp pages through url. I want to stop url access. I saw some example it shows the usage of filters. I'm new to filters I don't how to implement it. I'm using spring mvc
I think you want to use Spring security, you can check their official documentation
I think what you want is something that can be placed in your configuration class of WebSecurityConfigurerAdapter
In there, you can protect your pages from unauthenticated and unauthorized access by providing your implementation of configure(HttpSecurity http) method. This is the sample they provide on their documentation page:
package hello;
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.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}

How use Spring Boot oAuth2 + Azure AD in a stateless micro-service?

When I try the sample code from Microsoft Azure to use oAuth2 and Spring Boot, it uses a stateful session to check authentication/authorization. You can see this in that:
It never passes any headers/JWT on any calls
It has a cookie "JSESSIONID" that you can use in new postman session (after obtaining it in a different browser) and it will consider you logged in
This will not work as our micro-services will be multiple instances.
How could I convert this to use a JWT (Authorization: Bearer AQab...) for subsequent calls instead of the cookie?
Dependencies:
//All using Spring Boot 2.0.5.RELEASE
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-webflux')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.security:spring-security-oauth2-client')
compile('org.springframework.security:spring-security-oauth2-jose')
//Using 2.0.7
compile('com.microsoft.azure:azure-active-directory-spring-boot-starter')
Config:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
#EnableWebSecurity
#EnableGlobalMethodSecurity( prePostEnabled = true )
public class OAuthConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private OAuth2UserService<OidcUserRequest, OidcUser> userService;
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService( userService );
}
}
Controller:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class MainController
{
private OAuth2AuthorizedClientService clientService;
#Autowired
public MainController(OAuth2AuthorizedClientService clientService)
{
this.clientService = clientService;
}
#GetMapping( "checkrole" )
#ResponseBody
#PreAuthorize( "hasRole('ROLE__Test')" )
public String group1()
{
return "ok";
}
#GetMapping( "/" )
#ResponseBody
public String getUser(OAuth2AuthenticationToken userToken)
{
//Printing out the oAuth token just for testing
return clientService.loadAuthorizedClient(
userToken.getAuthorizedClientRegistrationId(),
userToken.getName()
).getAccessToken().getTokenValue();
}
}
application.yml:
spring:
security:
oauth2:
client:
registration:
azure:
client-id: ${YOUR_CLIENT_ID:}
client-secret: ${YOUR_CLIENT_SECRET:}
azure:
activedirectory:
tenant-id: ${YOUR_TENANT_OR_DIRECTORY_ID:}
active-directory-groups: Test
Complete Sample Code
https://github.com/Microsoft/azure-spring-boot/tree/master/azure-spring-boot-samples/azure-active-directory-spring-boot-backend-sample

How to set WebSSOProfileConsumerImpl in your Spring Configuration file

I am facing issue of specifying WebSSOProfileConsumerImpl to my Spring Configuration file. I am trying to modify responseSkew in this bean but after adding configuration for WebSSOProfileConsumerImpl I am getting MetadataManager issue
APPLICATION FAILED TO START
Description:
Parameter 0 of method setMetadata in org.springframework.security.saml.websso.AbstractProfileBase required a bean of type 'org.springframework.security.saml.metadata.MetadataManager' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.saml.metadata.MetadataManager'
Can anyone help me in resolving this issue?
I have already gone through the link : http://docs.spring.io/spring-security-saml/docs/current/reference/html/configuration-advanced.html but it does not specify how to set this in configuration.
My Code
import static org.springframework.security.extensions.saml2.config.SAMLConfigurer.saml;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.saml.websso.WebSSOProfileConsumer;
import org.springframework.security.saml.websso.WebSSOProfileConsumerImpl;
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Value("${security.saml2.metadata-url}")
String metadataUrl;
#Value("${server.ssl.key-alias}")
String keyAlias;
#Value("${server.ssl.key-store-password}")
String password;
#Value("${server.port}")
String port;
#Value("${server.ssl.key-store}")
String keyStoreFilePath;
#Value("${security.saml2.responseSkew}")
int responseSkew = 0;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/saml*").permitAll()
.anyRequest().authenticated()
.and()
.apply(saml())
.serviceProvider()
.keyStore()
.storeFilePath(this.keyStoreFilePath)
.password(this.password)
.keyname(this.keyAlias)
.keyPassword(this.password)
.and()
.protocol("https")
.hostname(String.format("%s:%s", "localhost", this.port))
.basePath("/")
.and()
.identityProvider()
.metadataFilePath(this.metadataUrl).and();
/* Map<? extends Object, Object> sharedObjects = new Map<? extends Object>, Object>(http.getSharedObjects());
sharedObjects.put(WebSSOProfileConsumer.class, webSSOprofileConsumerImpl());*/
}
#Bean
#Qualifier("webSSOprofileConsumer")
public WebSSOProfileConsumer webSSOprofileConsumerImpl() {
WebSSOProfileConsumerImpl consumerImpl = new WebSSOProfileConsumerImpl();
consumerImpl.setResponseSkew(this.responseSkew);
return consumerImpl;
}
}
If you do not need the WebSSOProfileConsumer to be accessible as a bean to the rest of the application, you can create it inside the configure method like this:
#Override
protected void configure(final HttpSecurity http) throws Exception {
WebSSOProfileConsumerImpl consumerImpl = new WebSSOProfileConsumerImpl();
consumerImpl.setResponseSkew(this.responseSkew);
http
.authorizeRequests()
.antMatchers("/saml*").permitAll()
.anyRequest().authenticated()
.and()
.apply(saml())
.serviceProvider()
.ssoProfileConsumer(consumerImpl) // <-- added here
.keyStore()
// Your method continues as before
I do not remember the exact reason regular wiring fails in your case, but the gist of it is that configure sets up a builder object rather than regular beans. Normally the ServiceProviderBuilder provides the MetadataManager needed by your WebSSOProfileConsumer during its build step, but if you autowire the profile consumer you won't get the MetadataManager provided for you since your autowired object is assumed to be complete already.
(I learned this when looking for a way to configure the max authentication age, since the two hour default in spring-security-saml in some cases is lower than the defaults used by identity providers. This effectively locks your users out of your application until the identity provider's max authentication age has passed.)

Resources