Spring Boot Security - Postman gives 401 Unauthorized - java

I am developing rest APIs in Spring Boot. I am able to do CRUD operations and postman gives correct responses, but when I add Spring Security username and password Postman gives 401 Unauthorized.
I have provided a spring boot security username and password as below.
application.proptries
spring.jpa.hibernate.ddl-auto=update
spring.datasource.platform=mysql
spring.datasource.url=jdbc:mysql://localhost:3306/pal?createDatabaseIfNotExist=true
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.security.user.name=root
spring.security.user.password=root
I have done basic auth with username as root and password as root.
Preview request gives headers updated successfully message :
EDIT
I have deleted the cookies in postman but still facing the same issue
SecurityConfing.java
My Security Configuration are as below.
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
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;
#Configuration
#EnableWebSecurity
#Order(1000)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
public void configureGlobal(AuthenticationManagerBuilder authenticationMgr) throws Exception {
authenticationMgr.jdbcAuthentication().dataSource(dataSource())
.usersByUsernameQuery(
"select email,password from user where email=? and statusenable=true")
.authoritiesByUsernameQuery(
"select email,role from user where email=? and statusenable=true");
System.out.println(authenticationMgr.jdbcAuthentication().dataSource(dataSource())
.usersByUsernameQuery(
"select email,password from user where email=? and statusenable=true")
.authoritiesByUsernameQuery(
"select email,role from user where email=? and statusenable=true"));
}
#Bean(name = "dataSource")
public DriverManagerDataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/pal");
driverManagerDataSource.setUsername("root");
driverManagerDataSource.setPassword("");
return driverManagerDataSource;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.authorizeRequests().antMatchers("/admin/**").hasAnyRole("ROLE_ADMIN","ROLE_USER").anyRequest().permitAll()
.and()
.authorizeRequests().antMatchers("/user/**").hasAnyRole("ROLE_USER").anyRequest().permitAll();
}

#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers(HttpMethod.POST,"/newuser").permitAll()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers(HttpMethod.POST,"/newuser/*").permitAll()
.antMatchers(HttpMethod.GET,"/master/*").permitAll()
.antMatchers(HttpMethod.GET,"/exploreCourse").permitAll()
.anyRequest().authenticated()
}
}
You need to configure Spring Security, by default all routes all secured for authrorization.
Please have a look JWT Token implementation at this Link.

If Authorization needed in spring boot, the below annotation at root configuration class.
#EnableAuthorizationServer
( and other required annotations)
public class Application{
....
....
}
Below dependency also needed to be added
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>

Related

401 unauthorized even with correct username and password

I'm creating an user API and trying to implement an authentication method through Spring Boot security.
Even using the correct password and the default Spring Security user user, my Postman still gives me an authorization error. I can't see where the problem is in this code.
Security config:
package com.api.business_products_management.config;
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 SecurityConfig {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
return http.build();
}
}
Controller:
package com.api.business_products_management.controllers;
import com.api.business_products_management.dtos.UserDto;
import com.api.business_products_management.models.UserModel;
import com.api.business_products_management.services.UserService;
import jakarta.validation.Valid;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
#RestController
#CrossOrigin(origins = "*", maxAge = 3600)
#RequestMapping("/user")
public class UserController {
private BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
#PostMapping
public ResponseEntity<Object> saveUser(#RequestBody #Valid UserDto userDto) {
var userModel = new UserModel();
BeanUtils.copyProperties(userDto, userModel);
userModel.setPassword(passwordEncoder().encode(userModel.getPassword()));
return ResponseEntity.status(HttpStatus.CREATED).body(userService.save(userModel));
}
}
Console:
2023-02-17T18:24:13.152-03:00 INFO 8418 --- [nio-8099-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-02-17T18:24:13.152-03:00 INFO 8418 --- [nio-8099-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-02-17T18:24:13.154-03:00 INFO 8418 --- [nio-8099-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
Without seeing your Postman request, my initial guess would be that it's the lack of CSRF (from your Postman request) which is causing the 401. You can read more about CSRF within Spring here: https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/csrf.html
From that document:
As of Spring Security 4.0, CSRF protection is enabled by default
You can test this theory by temporarily disabling CSRF as shown in the example configuration here:
https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/csrf.html#csrf-configure
i.e.:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
return http.build();
}
}
I'd highly recommend reading the rest of the documentation to familiarize yourself with what CSRF protects against and whether it's an acceptable risk to turn it off (or selectively turn disable for certain paths) prior to disabling it in a production environment though.

Spring Security CORS: Origin has been blocked by CORS Policy

I'm using spring boot for the first time in a project with angular, everything was working fine until I added the spring security dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
Now I get this error in the client side:
Access to XMLHttpRequest at 'http://localhost:8080/api/v1/login' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
I tried to change the config as the documentation suggest so I added class
src/main/java/com/example/securingweb/WebSecurityConfig.java
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer{
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
I also have this in my controller:
#CrossOrigin(origins = "http://localhost:4200")
As you have added Spring security dependency, so spring will enable Basic Auth which will validate your each and every request. And that enable CORS(Cross Origin Request Sharing) aswell. Though you have added CrossOrigin for every request that is not enough to disable the CORS.
More Details About CORS
So either you need to send the spring security generated token which will print on your console
or
you need to configure Spring security Configuration class which will validate your authetication or permit the specific url.
More about Spring Security here
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
#Configuration
public class CorsConfig {
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("GET","POST"));
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
import org.springframework.context.annotation.Bean;
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.builders.WebSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.filter.CorsFilter;
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/h2-console/**"
);
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/offerTransactionCall").permitAll()
.anyRequest().authenticated();
}
}

Spring Security: Convert in-memory auth to database

I'm using in-memory auth to have my login working in spring
However, I want to change it now to persistent database
Please see code below:
JWTWebSecurityConfig
package com.sbc.cpex.security.jwt;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.builders.WebSecurity;
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.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class JWTWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtInMemoryUserDetailsService;
#Autowired
private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter;
#Value("${jwt.get.token.uri}")
private String authenticationPath;
#Autowired
DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// auth
// .userDetailsService(jwtInMemoryUserDetailsService)
// .passwordEncoder(passwordEncoderBean());
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password"
+ " from users where username=?")
.passwordEncoder(passwordEncoderBean());
}
#Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
// .csrf().and().cors().disable()
.exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity
.headers()
.frameOptions().sameOrigin() //H2 Console Needs this setting
.cacheControl(); //disable caching
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.antMatchers(
HttpMethod.POST,
authenticationPath
)
.antMatchers(HttpMethod.OPTIONS, "/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/" //Other Stuff You want to Ignore
)
.and()
.ignoring()
.antMatchers("/h2-console/**/**");//Should not be in Production!
}
}
Steps I did:
1. Comment out these lines
auth
.userDetailsService(jwtInMemoryUserDetailsService)
.passwordEncoder(passwordEncoderBean());
Add the following lines for jdbcauth
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password"
+ " from users where username=?")
.passwordEncoder(passwordEncoderBean());
Create a class called DataSourceConfig
package com.sbc.cpex.security.jwt;
import javax.sql.DataSource;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.username("test");
dataSourceBuilder.password("pass");
return dataSourceBuilder.build();
}
}
But I'm getting this error
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'h2Console' defined in class path resource [org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.ServletRegistrationBean]: Factory method 'h2Console' threw exception; nested exception is java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
Please enlighten me. TIA
The stack trace itself is self explanatory :
nested exception is java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
You need to provide the Datasource url when creating a bean of Datasource.
From the stacktrace I could understand that you are using H2 . So, you could create a bean like :
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("username");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
In your code, you only provided the username and password part, hence it is throwing the error.
#iamjpcbau Springboot is autoconfiguring H2 as the databse as it is found as a dependency during class path scanning. Since you have provided a Datasource bean , spring automatically takes it up for configuring H2 but the url is missing , which causes the exception that you are receiving.
Inorder to configure another database with your project, configure the database through application.properties or application.yml or manually create configuration beans so that the configuration for your corresponding database is taken up at startup instead of H2.Now, since there are no other database configured and since H2 is found on classpath , spring is configuring that by default.

SoapUI - send login details in JSON

I'm testing a REST sevice made in Spring using SoapUI. When I don't have a username/login, I'm able to send HTTP requests successfully. However, when I provide Spring Security like below:
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.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 SpringSecurityConfig extends WebSecurityConfigurerAdapter {
// Create 2 users for demo
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER")
.and()
.withUser("admin").password("{noop}password").roles("USER", "ADMIN");
}
// HTTP Basic Auth for endpoints
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//HTTP Basic authentication
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/books/**").hasRole("USER")
.antMatchers(HttpMethod.POST, "/books").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/books/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PATCH, "/books/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/books/**").hasRole("ADMIN")
.and()
.csrf().disable()
.formLogin().disable();
}
}
I keep getting error 401. I've tried hard-coding a password, setting Auth and setting paramaters in SoapUI. Can anyone advise on how to include user and password in test?
It is a 2 step process.
1) Generate the token using user/pass via some online service like:- https://www.blitter.se/utils/basic-authentication-header-generator/
eg. for user/pwd asdf/asdf it generates:-
Authorization: Basic YXNkZjphc2Rm
Here Authorization is your header key and Basic YXNkZjphc2Rm is header value
2) Add it to the rest endpoint like below and make your call.

How to change roles and permissions in runtime in Spring-boot using SpringWebSecurity

I have same "projects" and same users in database in my Spring-boot webapp. Users access to projects using URLs like:
http: // server / project / 1
http: // server / project / 2
...
http: // server / project / x
Where "x" is the project ID as type long.
Different users have different privileges to projects, eg .:
User1 has ADMIN role in "Project1"
User2 has USER role in "Project1"
User3 has the USER role in "Projec1"
User1 has USER role in "Project2"
User2 has ADMIN role in "Project2"
User3 has the USER role in "Projec2"
Some user add new project "Project3" and grant access to this project to User1 and User2 with the USER and User3 roles with the ADMIN role.
In database I have table USERS (user list) and table AUTHORITIES (privileges of each user) but I don't know how to link privileges, users and "projects".
package pl.pecynki.testapp;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
#Autowired
private DataSource dataSource;
#Bean
public AuthenticationSuccessHandler successHandler() {
SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
handler.setUseReferer(false);
return handler;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/project/**")
.hasRole("USER");
http
.authorizeRequests()
.antMatchers("/project/**/edit/")
.hasRole("ADMIN");
http
.authorizeRequests()
.antMatchers("/project/new")
.hasRole("SUPERADMIN");
http
.authorizeRequests()
.antMatchers("/notice_form")
.authenticated();
http
.authorizeRequests()
.antMatchers("/","/**")
.permitAll();
http
.formLogin()
.loginPage("/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(successHandler());
http
.formLogin()
.permitAll();
http
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username,password,enabled from users where username=?")
.authoritiesByUsernameQuery("select username,authority from authorities where username=?");
}
}

Categories