Spring Security make username and password auth at specific path - spring

my spring-boot project is based on REST and JWT authentication. Now I would like to disable the JWT auth for a specific path and instead of it authenticate by simple username and password. Is it achievable ?
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login/**").permitAll()
.antMatchers("/register/**").permitAll()
.antMatchers("/api/jwt/**").authenticated()
.anyRequest().authenticated().and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).csrf().disable();
}
I want to add auth by username and password for example to "/api/data/**".
EDIT:
My first config with #Order(1)
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("abc").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/data/**").hasRole("ADMIN")
.and()
.httpBasic()
.and()
.csrf().disable();
}
It works, but when the request with correct credentials to api/data/** is executed, the jwtAuthenticationFilter() fires also.

Implement AuthenticationProvider class for custom authentication using username and password. Then use the authentication provider like -
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
#Override
protected void configure(
#NotNull AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(
HttpSecurity http) throws
Exception {
http.requestMatchers()
.antMatchers("/api/data/**")
.and()
.authorizeRequests()
.anyRequest()
.permitAll()
.and()
.csrf()
.disable()
.formLogin().disable()
.and()
.authenticationProvider(authenticationProvider);
}

Related

Custom login REST endpoint

I am trying to change default POST /login spring security endpoint for logging in to POST /api/users/login
I already tried doing that through
formLogin().loginProcessingUrl("/api/users/login")
or
formLogin().loginPage("/api/users/login")
but it didn't work. How do i do that? I can't find tutorial describing it or stack overflow answer. I also tried reading Spring Security documentation but it didn't help too.
My security config looks like this:
private final MyUserDetailsService myUserDetailsService;
private final PasswordEncoder passwordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//go from most restrictive url to least restrictive, from single urls to /**
httpSecurity.csrf().disable().cors().and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//to allow h2 console page
httpSecurity.headers().frameOptions().disable();
}
In the WebSecurityConfigurerAdapter class there is to configure methods that you should override like so:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(
unAuthorizedResponseAuthenticationEntryPoint)
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
http.addFilterBefore(authenticationTokenFilter,
UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring().antMatchers(HttpMethod.POST, "/api/auth/login")
.antMatchers(HttpMethod.OPTIONS, "/**")
.and().ignoring()
.antMatchers(HttpMethod.GET, "/").and().ignoring();
}
}
Please see in method:
public void configure(WebSecurity webSecurity) throws Exception
the line:
webSecurity.ignoring().antMatchers(HttpMethod.POST, "/api/auth/login")
This is where you put your custom authentication API endpoint!
And then you may proceed as I explained in the following question
How to use custom UserDetailService in Spring OAuth2 Resource Server?

Spring Security Multiple HTTPSecurity with Different User Details Services Not Working in Spring Boot

I have two types of users: Application User and End User and I have separate tables for these. Now, I want to apply security on these two tables.
I provided custom implementation of UserDetailsService for Application users:
#Component("applicationUserDetailsService")
public class ApplicationUserDetailsService implements UserDetailsService {}
And, I provided another second custom implementation of UserDetailsService for End users:
#Component("endUserDetailsService")
public class EndUserDetailsService implements UserDetailsService {}
Now, in the following code snippet, I have registered two endpoints for both type of users. I have injected both implementation of UserDetailsService and registered by #Overide configure(AuthenticationManagerBuilder auth) method for both application and end user separately.
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Import(SecurityProblemSupport.class)
public class SecurityConfiguration {
// Injected via Constructor Injection
private final EndUserDetailsService endUserDetailsService;
private final ApplicationUserDetailsService applicationUserDetailsService;
#Configuration
#Order(1)
public class ApplicationUserSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/api/customer/**")
.authorizeRequests()
.antMatchers("/api/customer/authenticate").permitAll()
.antMatchers("/api/customer/**")
.authenticated()
.and()
.apply(securityConfigurerAdapter());
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(endUserDetailsService);
}
}
//no #Order defaults to last
#Configuration
public class EndUserSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.apply(securityConfigurerAdapter());
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(applicationUserDetailsService);
}
}
private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider);
}
}
And, I'm trying to authenticate the user like this:
//Injected via Constructor Injection
private final AuthenticationManagerBuilder authenticationManagerBuilder;
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
When the above code snippet is executed, I get the Null Pointer Exception because authenticationManagerBuilder.getObject() returns NULL. And when I use just when implementation of UserDetailService with #Component("userDetailsService") and not set UserDetailService in security config like auth.userDetailsService("..."), it works fine but by that way I can't achieve authentication from multiple tables.
What I want to Achieve:
In simple words, I want spring security to authenticate user from two tables.
requestMatchers() is the call that you need as it allows you to isolate adapters by URL:
#Order(1)
#EnableWebSecurity
class EndUserConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/api/customer/**")
.and()
.authorizeRequests()
.antMatchers("/**").hasRole("CUSTOMER")
.and()
.apply(yourJointConfigurations());
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(endUserDetailsService);
}
}
Regarding calling the AuthenticationManager directly, it would be ideal if you could rely on the existing filter chain to do the work for you. For example, since you are stateless, HTTP Basic might be a better fit for you, which you could apply to both configurations, instead of trying to have a dedicated /authenticate endpoint.

HttpSecurity Spring Boot configuration

I need to configure 3 endpoints, 2 with authentication and 1 without. The problem is I'm getting all the endpoints with 401 Unauthorized error.
/users no authentication need
/users/1 needs authentication
/details/1 needs authentication
I'm using the dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
And I implemented the class:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin")
.password("pwd")
.roles("USER", "ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/users").hasAnyRole("ADMIN", "USER")
.anyRequest().permitAll()
.and()
.authorizeRequests()
.anyRequest().hasAnyRole("ADMIN", "USER");
}
}
As per your requirement you just need simple http configuration where GET users can be accessed by all as public url and other need basic auth..below will work for you.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/users").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}

Spring security Basic Auth and Form login for the same API

I would like to access all my API's via two authentication mechanisms, Basic Auth & Form login. I know that there are existing questions, but, the answers did not work for me, and my use case is a little bit different.
My config:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
#Configuration
#Order(1)
public static class SecurityConfigBasicAuth extends WebSecurityConfigurerAdapter {
final private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
public SecurityConfigBasicAuth(RestAuthenticationEntryPoint restAuthenticationEntryPoint,
#Qualifier("customUserDetailsService") UserDetailsService userDetailsService) {
this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
// #Bean authenticationProvider()
// #Bean passwordEncoder()
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.formLogin().disable()
.logout().disable();
}
}
#Configuration
public static class SecurityConfigFormLogin extends WebSecurityConfigurerAdapter {
final private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
final private RestfulSavedRequestAwareAuthenticationSuccessHandler restfulSavedRequestAwareAuthenticationSuccessHandler;
final private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
public SecurityConfigFormLogin(RestAuthenticationEntryPoint restAuthenticationEntryPoint,
RestfulSavedRequestAwareAuthenticationSuccessHandler restfulSavedRequestAwareAuthenticationSuccessHandler,
CustomAuthenticationProvider hashAuthenticationProvider) {
this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
this.restfulSavedRequestAwareAuthenticationSuccessHandler = restfulSavedRequestAwareAuthenticationSuccessHandler;
this.customAuthenticationProvider = customAuthenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.cors()
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.csrf().disable()
.httpBasic().disable()
.formLogin()
.usernameParameter("id1")
.passwordParameter("Id2")
.loginProcessingUrl("/test/login")
.successHandler(restfulSavedRequestAwareAuthenticationSuccessHandler)
.failureHandler(myFailureHandler())
.and()
.logout();
}
// #Bean myFailureHandler()
}
}
As you can see, I defined two 'WebSecurityConfigurerAdapters', one for Basic Auth, and one for Form login. The Form login is REST compatible (does not redirect, but gives HTTP responses).
The problem is as follows: The first 'WebSecurityConfigurerAdapter' that is loaded works and overrides the second. The above example, makes it possible to use basic auth, but I cannot login on POST '/test/login', I get a:
{
"timestamp": 1534164906450,
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/test/login"
}
Update fixed: the key was to use the 'requestMatchers()', see answer section for solution (as suggested by jzheaux)
Okay, this is how I fixed this:
I configured the Basic Auth configuration as:
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/api/**")
.and()
.cors()
.and()
.csrf().disable()
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and();
}
If you do not want that the basic authentication returning new cookie with new JSESSIONID, add:
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.sessionFixation()
.migrateSession()
The Form login configuration as:
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers(HttpMethod.POST, "/test/login")
.and()
.cors()
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.formLogin()
.usernameParameter("id1")
.passwordParameter("id2")
.loginProcessingUrl("/test/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(myFailureHandler())
.and()
.logout();
}
Now, it is possible for me to authenticate via the Form login configuration, and use the cookie session id to call /api/** (configured in the Basic Auth configuration). I can also just use the Basic Auth authentication ofcourse.

AuthenticationSuccessHandler in Spring MVC Java based configuration

I have three roles, and i want to redirect user to different pages after login according to their roles. I know this can be done by AuthenticationSuccessHandler, but I am having trouble in declaring it in Java based configuration.
So far I have done this.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/login").permitAll()
.antMatchers("/admin/**").hasRole("USER")
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.successHandler(successHandler) //----- to handle user role
.failureUrl("/loginfailed")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.deleteCookies("JSESSIONID")
.invalidateHttpSession( true )
.and();
}
My question is where to declare successHandler and how to autowire it in this class, or how to declare successHandler method in this class and use it.
Try this: Moving Spring Security To Java Config, where does authentication-success-handler-ref go?
Code from the post above:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("")
.defaultSuccessUrl("/")
.failureUrl("")
.successHandler(//declare your bean here)
.and()
.logout()
.permitAll()
.and()
}
Then in the authentication handler you can apply the required logic
public class MYSuccessHandler implements AuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
handle(request, response, authentication);
}
protected void handle(HttpServletRequest request,
// logic
redirectStrategy.sendRedirect(request, response, targetUrl);
}
/** Builds the target URL according to the logic defined in the main class Javadoc. */
protected String determineTargetUrl(Authentication authentication) {
}
}
Tutorial listed here http://www.baeldung.com/spring_redirect_after_login

Resources