HttpSecurity Spring Boot configuration - spring-boot

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

Related

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.

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.

Spring permitAll() and ignorning() doesn't work

I'm trying to configure Spring-Boot 2 application with in-memory authorization and it drive me crazy...
I need to secure all resources except /news.
I have created this configuration beans following the Spring's docs but Spring Security isn't permit GET method on /news:
#Slf4j
#Configuration
#EnableWebSecurity
public class ApplicationAuthentication extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers(HttpMethod.GET,"/news");
}
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER")
.and()
.withUser("admin")
.password("admin")
.roles("USER", "ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET,"/news")
.permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
I'm using spring-boot 2.0.4.RELEASE,
Can someone help me?

Spring Security make username and password auth at specific path

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

spring websocket no spring security

I used spring websocket by stomp and spring message.
I don't like using spring security.
When I use:
SimpMessageSendingOperations.convertAndSendToUser(username,"");
I must :
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // Refactor login form
// See https://jira.springsource.org/browse/SPR-11496
.headers().addHeaderWriter(
new XFrameOptionsHeaderWriter(
XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN)).and()
.formLogin()
.defaultSuccessUrl("/index.html")
.loginPage("/login.html")
.failureUrl("/login.html?error")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/")
.logoutUrl("/logout.html")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/static/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.anyRequest().authenticated()
.and();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("lihui").password("1234").roles("USER").and()
.withUser("paulson").password("bond").roles("ADMIN","USER");
}
}
So, what can I do? Please help.
//when i not useing spring security.i can used
simpMessageSendingOperations.convertAndSendTo(usename,"queue/"+usename+"/greating",object);
//and client like this:
stompClient.subscribe('/queue/'++usename+'greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});

Resources