Http Basic authentication using Spring Security with Angular 6 - spring-boot

I am developing a Angular 6 application for which I am using Spring Boot for REST services And using Spring security Basic authentication for authorization purpose.
The issue is , basic authentication is not working and it shows pop up everytime i login into the application. I am using interceptor in angular 6 for adding the authorization header.
Can anybody please help as to where I am going wrong ?
Code :
#Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
let username=localStorage.getItem("username")
let password=localStorage.getItem("password")
const customReq = req.clone({
headers: req.headers.set("Authorization", "Basic " + btoa(username+":"+password) )
});
return next.handle(customReq);
}
constructor() { }
}
And the spring boot code
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.httpBasic().and().authorizeRequests().antMatchers("/index.html", "/", "/home/**", "/login").permitAll()
.anyRequest().authenticated().and().csrf().disable().httpBasic();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
}
Since I am new to Spring please let me know if you need any other files.

This generally happens when you #SpringBootApplication class is in one package and your SecurityConfig class is not present in sub package.
For example, if Application is in com.example.app package and your security config is in com.security.example then Spring cannot find your configuration and goes ahead with Spring security defaults like form based login page.
Please check the package structure and add #ComponentScan if requried.

Related

Spring Security with OAuth2(Keycloak) disable default login page

I have successfully configured Spring Boot Spring Security with Keycloak. Everything works fine. In order to login, I use the following URL: http://localhost:8081/realms/MY_REALM_NAME
But when I try to access the following page: http://localhost:8080/login I see the following page:
I'd like to disable/remove this page. How to properly configure it with Spring Security?
UPDATED
My SpringSecurity configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends VaadinWebSecurityConfigurerAdapter {
private final ClientRegistrationRepository clientRegistrationRepository;
private final GrantedAuthoritiesMapper authoritiesMapper;
private final ProfileService profileService;
SecurityConfiguration(ClientRegistrationRepository clientRegistrationRepository,
GrantedAuthoritiesMapper authoritiesMapper, ProfileService profileService) {
this.clientRegistrationRepository = clientRegistrationRepository;
this.authoritiesMapper = authoritiesMapper;
this.profileService = profileService;
SecurityContextHolder.setStrategyName(VaadinAwareSecurityContextHolderStrategy.class.getName());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
// Enable OAuth2 login
.oauth2Login(oauth2Login ->
oauth2Login
.clientRegistrationRepository(clientRegistrationRepository)
.userInfoEndpoint(userInfoEndpoint ->
userInfoEndpoint
// Use a custom authorities mapper to get the roles from the identity provider into the Authentication token
.userAuthoritiesMapper(authoritiesMapper)
)
// Use a Vaadin aware authentication success handler
.successHandler(new KeycloakVaadinAuthenticationSuccessHandler(profileService))
)
// Configure logout
.logout(logout ->
logout
// Enable OIDC logout (requires that we use the 'openid' scope when authenticating)
.logoutSuccessHandler(logoutSuccessHandler())
// When CSRF is enabled, the logout URL normally requires a POST request with the CSRF
// token attached. This makes it difficult to perform a logout from within a Vaadin
// application (since Vaadin uses its own CSRF tokens). By changing the logout endpoint
// to accept GET requests, we can redirect to the logout URL from within Vaadin.
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
);
}
#Bean
#Primary
public SpringViewAccessChecker springViewAccessChecker(AccessAnnotationChecker accessAnnotationChecker) {
return new KeycloakSpringViewAccessChecker(accessAnnotationChecker, "/oauth2/authorization/keycloak");
}
private OidcClientInitiatedLogoutSuccessHandler logoutSuccessHandler() {
var logoutSuccessHandler = new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);
logoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}");
return logoutSuccessHandler;
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
// Don't apply security rules on our static pages
web.ignoring().antMatchers("/session-expired");
}
#Bean
public PolicyFactory htmlSanitizer() {
// This is the policy we will be using to sanitize HTML input
return Sanitizers.FORMATTING.and(Sanitizers.BLOCKS).and(Sanitizers.STYLES).and(Sanitizers.LINKS);
}
}
Have tried formLogin().disable() method?
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
//your config here
.and().formLogin().disable();
}

How to configure ldap in spring-security 5.7 while retaining basic form login

I'm trying to configure my webSecurity to use both ldap and basic authentication (jdbc) with the new component-based security configuration (no WebSecurityConfigurerAdapter) but I can't get it to use both.
The required result is for spring to first attempt ldap, and if it doesn't find (or just fails for now is good enough) attempt to login using basic autentication.
The project is a migration from an older Spring-Boot version and with WebSecurityConfigurerAdapter the following code is what worked:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests().antMatchers("/services/**").permitAll().anyRequest().authenticated();
http.httpBasic();
http.formLogin().permitAll().loginPage("/login").defaultSuccessUrl("/customer/overview", true);
http.logout().permitAll();
http.csrf().disable();
http.headers().frameOptions().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetails);
//#formatter:off
auth.ldapAuthentication()
.userSearchFilter("(uid={0})")
.userSearchBase("ou=people")
.groupSearchFilter("(uniqueMember={0})")
.groupSearchBase("ou=groups")
.groupRoleAttribute("cn")
.rolePrefix("ROLE_")
.userDetailsContextMapper(customLdapUserDetailsContextMapper())
.contextSource()
.url(ldapUrl);
//#formatter:on
}
#Bean
CustomLdapUserDetailsContextMapper customLdapUserDetailsContextMapper()
{
CustomLdapUserDetailsContextMapper mapper = new CustomLdapUserDetailsContextMapper();
mapper.setCustomUserDetailsService(userDetailsService());
return mapper;
}
//Implementation of custom contextMapper is not relevant for example i believe, basicly it maps some ldap roles, but for testing i don't use roles yet
}
and this is what my conversion to the new style looks like:
#Configuration
public class WebSecurityConfig
{
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager ldapAuthenticationManager) throws Exception
{
// #formatter:off
http.authorizeRequests()
.mvcMatchers("/services/**").permitAll()
.mvcMatchers("/resources/**").permitAll()
.mvcMatchers("/webjars/**").permitAll()
.anyRequest().authenticated();
http.httpBasic();
http.formLogin().permitAll().loginPage("/login").defaultSuccessUrl("/customer/overview", true);
http.logout().permitAll();
http.csrf().disable();
http.authenticationManager(ldapAuthenticationManager); //THIS LINE SEEMS TO BE PROBLEMATIC
// #formatter:on
return http.build();
}
#Bean
public AuthenticationManager ldapAuthenticationManager(BaseLdapPathContextSource ldapContextSource, UserDetailsService userDetailsService)
{
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(ldapContextSource);
UserDetailsServiceLdapAuthoritiesPopulator ldapAuthoritiesPopulator = new UserDetailsServiceLdapAuthoritiesPopulator(userDetailsService);
factory.setUserSearchFilter("(uid={0})");
factory.setUserSearchBase("ou=people");
factory.setLdapAuthoritiesPopulator(ldapAuthoritiesPopulator);
return factory.createAuthenticationManager();
}
}
when in the above new code the line http.authenticationManager(ldapAuthenticationManager); is enabled ldap login works fine (and it even binds roles from database user), but basic login doesn't work. however when the line is disabled basic login works but ldap does not.
Any help on how to get spring to use both logins would be much appreciated.
Instead of creating a custom AuthenticationManager, you can create the AuthenticationProvider that will be used for LDAP authentication.
You can configure the provider on HttpSecurity:
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, LdapAuthenticator authenticator) throws Exception {
// ...
http.authenticationProvider(
new LdapAuthenticationProvider(authenticator, ldapAuthoritiesPopulator));
// ...
return http.build();
}
#Bean
BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
authenticator.setUserSearch(
new FilterBasedLdapUserSearch("ou=people", "(uid={0})", contextSource));
return authenticator;
}

Evaluate Web Services Interceptor Before Spring Security Filter Chain

I have a SOAP-based web services application which is leveraging Spring Web Services (and Spring WS Security) as well as Spring Security. I am using a custom AbstractWsSecurityInterceptor to authenticate the incoming requests (using an injected AuthenticationManager) and to add the successful authentications to the SecurityContext. I then have a custom AcessDecisionManager which is using a custom WebSecurityExpressionHandler to validate a certain property from the principal added to the context by the interceptor.
Below is an idea of what my configuration files look like:
SecurityConfig.java:
#Getter
#Setter
#Configuration
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AccessDecisionManager customAccessDecisionManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
cors()
.and().csrf().disable()
.authorizeRequests()
.accessDecisionManager(customAccessDecisionManager)
.antMatchers(GET, "/actuator/**").permitAll()
.anyRequest().access("customAccessMethod()")
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
WebServiceConfig.java:
#EnableWs
#Configuration
#RequiredArgsConstructor
public class WebServiceConfig extends WsConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
...
...
#Bean
AbstractWsSecurityInterceptor customAuthenticationInterceptor() {
return new CustomAuthenticationInterceptor(authenticationManager);
}
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(customAuthenticationInterceptor());
}
}
The issue with this setup is that the Spring Security filter chain is evaluated first and fails the authentication because the AccessDecisionManager is evaluated before the request has a chance to enter the custom AbstractWsSecurityInterceptor and place the authentication in the SecurityContext.
Is there any way to evaluate the interceptor and handling of the request on the Web Services and WS Security side of things before it then hits the Spring Security filter chain? Is this a possibility?
Thank you in advance for the help!

Can not retrieve Principal from Spring Okta (CRA SPA)

I am currently trying to test out Okta with SPA front end (Create-React-App) and a Spring Boot application.
Currently I have the apps working, in that a user logins on the front end (via okta). The user can then access protected resources from server (spring boot). Hence the integration works well and nice.
My issue is I can't access the Principal on my Rest Controller.
ENV
Note: Spring-Security-Starter is NOT on the classpath just the OAuth2 autoconf
Spring Boot 2.0.6.RELEASE
okta-spring-boot-starter:0.6.1
spring-security-oauth2-autoconfigure:2.0.6.RELEASE'
Spring Configuration
okta.oauth2.issuer=https://dev-886281.oktapreview.com/oauth2/default
okta.oauth2.clientId={ clientId }
okta.oauth2.audience=api://default
okta.oauth2.scopeClaim=scp
okta.oauth2.rolesClaim=groups
security.oauth2.resource.user-info-uri=https://dev-886281.oktapreview.com/oauth2/default/v1/userinfo
Okta Service Configuration
Application type: Single Page App (SPA)
Allowed grant types: Implicit
Allow Access Token with implicit grant type: true
Controller
#RestController
#RequestMapping("/products")
public class ProductController {
...
#GetMapping
public ResponseEntity<List<ProductEntity>> getAllProducts(Principal principal) {
SpringBoot
#EnableResourceServer
#SpringBootApplication
public class CartyApplication {
public static void main(String[] args) {
SpringApplication.run(CartyApplication.class, args);
}
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
#Bean
protected ResourceServerConfigurerAdapter resourceServerConfigurerAdapter() {
return new ResourceServerConfigurerAdapter() {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated();
}
};
}
Once again the overall integration is working fine, users can only access protected resources once they've signed in via okta, I'm just wondering how to get the users details from okta on the controller.
Thanks in advance.
P.S soz for the code dump
EDIT: Removed snippets and added full CartyApplication class
EDIT2: Added repo - https://github.com/Verric/carty-temp
I have a feeling you might be missing this:
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
I'm guessing should remove the .antMatchers("/**").permitAll() line.
See: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#CO3-2
I'm guessing you want to protect all/most of your endpoints? I'd recommend only allowing specific routes, and protecting everything else.

Understanding Spring Boot's Oauth2 starter

I started off looking at the Oauth2 starter project and minimal configuration.
https://github.com/spring-projects/spring-security-oauth/blob/master/tests/annotation/jdbc/src/main/java/demo/Application.java
All the examples either use in memory configuration or jdbc configuration for storing client roles (e.g ClientDetailsServiceConfigurer) . In my case the details should come in LDAP. So I have two questions.
How do override the default to go to ldap instead of memory or jdbc.
In general , where how do I unravel the Spring Boot thread and read the starter source code and how to change default config ? All I see is a high level annotation.
org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer
This indirection in Spring Boot makes it extremely difficult to follow and scant documentation doesn't help. Or maybe I am missing something?
thanks !!! this has been bugging me for a while.
To implement Oauth2 with LDAP, you may follow this tutorial : https://raymondhlee.wordpress.com/2015/12/05/oauth2-authorization-server-with-spring-security.
You may also take a look a this other question: spring-security-oauth2 2.0.7 refresh token UserDetailsService Configuration - UserDetailsService is required
As for your other question "I want to follow the request and see what components get invoked and when": I suggest you add logging.
(1) Add logging in every method
(2) Set log level for security package in application.properties:
logging.level.org.springframework.security=DEBUG
(3) Add CommonsRequestLoggingFilter:
#Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
LOGGER.info("Creating CommonsRequestLoggingFilter");
CommonsRequestLoggingFilter crlf = new CommonsRequestLoggingFilter();
crlf.setIncludeClientInfo(true);
crlf.setIncludeQueryString(true);
crlf.setIncludePayload(true);
return crlf;
}
(4) Add log level for CommonsRequestLoggingFilter (in application.properties):
logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
For the OAuth/LDAP tutorial, here's the notable parts (quoted from https://raymondhlee.wordpress.com/2015/12/05/oauth2-authorization-server-with-spring-security):
Authorization Server Configuration Below is my implementation of the
AuthorizationServerConfigurerAdapter. The database schema for JDBC
client details and token services can be found in here.
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new JdbcTokenStore(dataSource)).authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
}
Login Security Configuration Below is the security configuration
handling user authorization.
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE) // note 1
public class LoginConfig extends WebSecurityConfigurerAdapter {
#Value("${ldap.domain}")
private String DOMAIN;
#Value("${ldap.url}")
private String URL;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel().anyRequest().requiresSecure();
// Only requests matching regex are handled by this security configurer
http.requestMatchers().regexMatchers("/login", "/login.+", "/oauth/.+", "/j_spring_security_check", "/logout"); //
AuthenticationEntryPoint entryPoint = entryPoint();
http.exceptionHandling().authenticationEntryPoint(entryPoint);
http.formLogin(); // note 3i
http.addFilter(usernamePasswordAuthenticationFilter());
http.authorizeRequests().antMatchers("/login").permitAll();
http.authorizeRequests().antMatchers("/oauth/**").authenticated();
http.authorizeRequests().antMatchers("/j_spring_security_check").anonymous().and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception { // note 4
authManagerBuilder.parentAuthenticationManager(authenticationManager());
}
protected AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(DOMAIN, URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
private AuthenticationEntryPoint entryPoint() {
return new LoginUrlAuthenticationEntryPoint("/login");
}
private UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() {
UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager();
AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler("/login?login_error=true");
filter.setAuthenticationFailureHandler(failureHandler);
return filter;
}
}

Resources