Spring Custom Security With MySQL And JPA Giving 403 Access Denied - spring-boot

I am trying to access my rest api on postman by providing authentication using UserDetailsService, but each time I am firing the request every time request giving 403 Access Denied. The behavior is same for POST and GET method. I have read the other issues logged on forum but every answers says it is due to CSRF, I disabled it but issue remains same.
Complete code is on : https://github.com/afulz29/spring-security-demo.git
Please help me, I am struggling with this issue since 3 days.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer{
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/api/**").authenticated().anyRequest().hasAnyRole("ADMIN");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*");
}
}
#RestController
#RequestMapping("/api")
public class UserController {
#Autowired
private UserService userService;
#GetMapping(path = "/users")
public User getUserById(#RequestParam("userId") Integer userId) {
return userService.getUserById(userId);
}
#PostMapping(path = "/users", consumes = MediaType.APPLICATION_JSON_VALUE)
public User addUser(#RequestBody User user) {
return userService.addUser(user);
}
}

I see couple of problems with your security config:
BASIC AUTH is not enabled but you are trying to do Basic Auth in postman
Do the following to enable Basic Auth
http
.authorizeRequests()
...
.and()
.httpBasic();
I guess the POST /api/users is a user registration endpoint. You must whitelist this endpoint so that anyone can register
http
.authorizeRequests()
.antMatchers( HttpMethod.POST,"/api/users").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().hasAnyRole("ADMIN")
.and()
.httpBasic();
Test:
Create user
POST: localhost:8080/api/users
{
"userName" : "user1",
"password": "pass"
}
Get user info
GET: localhost:8080/api/users?userId=1 //use the correct ID
With Basic Auth: userName = user1, password = pass
BONUS Feedback:
User.userName --> you might want to make this field unique
#Repository this annotation is not required in your Repository interfaces
UserService interface. I don't see any reason to use the interface and impl.

Related

Solution for dual security in spring boot application - OAuth2 (jwt token bearer) + X509 (certificates)

I tried to create a spring boot configuration with dual security checks on requests (Oauth2 token bearer and X509 certificates). I had 2 alternative ideas in mind, but cannot make it work either
dedicated endpoints for each type of security validation (/certif
for certification validation, /token for token validation)
all endpoints checked with either token or certificate validation
anything successfully would apply
This is my configuration that tries to achieve idea no 1:
#EnableResourceServer
#Configuration
#Order(1)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Value("${xxx.auth.resourceId}")
private String resourceId;
#Autowired
private DefaultTokenServices tokenServices;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceId)
.tokenServices(tokenServices)
.tokenExtractor(new BearerTokenExtractor());
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/unsecured/**")
.antMatchers("/token/**")
.and().authorizeRequests()
.antMatchers("/unsecured/**").permitAll()
.anyRequest().authenticated()
;
}
}
#EnableResourceServer
#Configuration
#Order(2)
public class X509ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/certif/**")
.and()
.authorizeRequests()
.antMatchers("/certif/**").hasAuthority("AUTH")
.and().x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)").userDetailsService(userDetailsService());
}
#Bean
public UserDetailsService userDetailsService() {
return new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) {
if (username.startsWith("xxx") || username.startsWith("XXX")) {
return new User(username, "",
AuthorityUtils
.commaSeparatedStringToAuthorityList("AUTH"));
}
throw new UsernameNotFoundException("User not found!");
}
};
}
}
For some reason I cannot make it work because filter OAuth2AuthenticationProcessingFilter seems to be deleting the authorization token created by filter X509AuthenticationFilter when I make a call with a certificate to /certif/info. I must mention that ResourceServerConfiguration is working ok when used alone and the /token/info endpoint is called with a token.
Mentioned filters are in spring-security-oauth:2.3.8 & spring-security-web:5.6.2
Orders have been changed in every direction but they seem to have no effect on how the filters are applied.
Any idea what is going on and how can I avoid this problem in order to achieve the desired behaviour?
You can try to config just one Configuration class.
You can join the two methods named as "configure", in just one.
I didn't test this code, but tell me if you did it working.
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/unsecured/**")
.antMatchers("/token/**")
.antMatchers("/certif/**")
.and().authorizeRequests()
.antMatchers("/unsecured/**").permitAll()
.anyRequest().authenticated()
.antMatchers("/certif/**").hasAuthority("AUTH")
.and().x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)").userDetailsService(userDetailsService());
;
}

Spring Boot Security UsernamePasswordAuthenticationFilter does not limit the login url to only POST

I looked all throughout and nobody else is having this issue that I can find. The authentication works correctly but the login url works on any HTTP method (GET, PUT, etc) vs. only working on POST. I tried manually setting filter.setPostOnly(true); on the custom JWTAuthenticationFilter I made, but it still allows on all methods.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String FILTER_PROCESS_URL = "/authentication";
private static final String HEALTH_RESOURCE_URL = "/health/**";
private CustomUserDetailService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurityConfig(CowCalfUserDetailService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, FILTER_PROCESS_URL).permitAll()
.antMatchers(HttpMethod.GET, HEALTH_RESOURCE_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(getJWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilter(new JWTAuthorizationFilter(authenticationManagerBean()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
public JWTAuthenticationFilter getJWTAuthenticationFilter() throws Exception {
final JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManagerBean());
filter.setPostOnly(true);
filter.setFilterProcessesUrl(FILTER_PROCESS_URL);
return filter;
}
}
If i understand you correctly, you want to limit the /authentication only to the POST Http-Method. If yes, you could achieve it by adding the following code snippet to your method in the "RestController" for the Authentication:
#RequestMapping(value = "/authentication", method = RequestMethod.POST)
RequestMapping Docs
I found out how setPostOnly() works. UsernamePasswordAuthenticationFilter.attemptAuthentication() checks for POST before attempting the authentication and throws an exception. This method I have overridden with my custom JWTAuthenticationFilter. I just did the same and added a check in my overridden method too. Thank you for the suggestions!

Oauth Spring Security not unauthorized to microservice oauth, registerController

I am beginner to Spring. I've been with this problem for several days and I wouldn't know how to solve it.
I have two microservices:
Microservice authentication : This allows return a JWT token to user when it access to path: /oauth/token
Microservice Account user : This microservice will have a lot of functions but the problem is it:
The user must be register in platform and this microservice call to server oauth to save the new created user.
So, I create the controller to microservice Oauth:
#PostMapping
#PreAuthorize("#oauth2.hasScope('server')")
public UserDto createUser(#Valid #RequestBody UserDto userDto) {
Usuario savedUser = new Usuario();
try {
savedUser = userService.create(this.toUsuario(userDto));
} catch (ArendheException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return toDto(savedUser);
}
The WebSecurityConfigureAdapter is:
#Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService usuarioService;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.usuarioService).passwordEncoder(passwordEncoder());
}
#Bean("authenticationManager")
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.POST,"/oauth/**").permitAll()
.antMatchers(HttpMethod.POST, "/user/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
As you can see, I write two antMatchers, the second to create a new user.
The problem is when I test it with Postman (path localhost:8005/user with data JSON parsed to POST method). The output is:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
I dont understand it because I have a permitAll to /user/** path.
Thanks.

Why can't I go to admin REST API request mapping after I have logged in - Spring Boot

I have some issues with my REST API, created from Spring Boot and Spring Security.
First I have created my Spring security configuration. As you see here, I have two paths with two different authorizations - USER and ADMIN.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private UserRepository userRepository;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Set the default standard admin and password as admin, if not exist
User user = userRepository.findByUsername("admin");
if(user == null) {
user = new User();
user.setUserID(0);
user.setUsername("admin");
user.setPassword(passwordEncoder().encode("admin"));
Set<Role> roles = new HashSet<Role>();
Role role = new Role();
role.setRoleID(0);
role.setRolename("ADMIN");
roles.add(role);
user.setRoles(roles);
userRepository.save(user);
}
// Connect our database to spring and also with a password encoder
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/user/**").authenticated().anyRequest().hasAnyAuthority("ROLE_USER");
http.authorizeRequests().antMatchers("/admin/**").authenticated().anyRequest().hasAnyAuthority("ROLE_ADMIN");
http.httpBasic();
http.formLogin().permitAll();
}
}
And this is my controllers both user and admin.
#RestController
#RequestMapping("/admin")
public class AdminController {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#PostMapping("/addUser")
public String addUser(#RequestBody User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(user);
return "Added user by admin";
}
#GetMapping("/adminHello")
public String adminHello() {
return "Admin say hello";
}
}
#RestController
#RequestMapping("/user")
public class UserController {
#GetMapping("/userHello")
public String userHello() {
return "processing..";
}
}
If I try to login to http://localhost:8080/login and write in my password and my username. Then I will be able to go in. Fine!
But these are the problems.
If I enter http://localhost:8080/user/userHello with a Admin account, I still get "processing..."
If I enter http://localhost:8080/admin/adminHello with a Admin account, I get
"Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri Sep 13 00:23:42 CEST 2019
There was an unexpected error (type=Forbidden, status=403).
Forbidden"
Why? Have I forgot something? My Accound have the ADMIN role in the database. Very clear.
Clearly your Role's are not working there is some issue with configuration.
Try this
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.formLogin().permitAll();
}

Get Currently HTTP basic authenticated User in Spring [duplicate]

This question already has answers here:
How to find out the currently logged-in user in Spring Boot?
(9 answers)
Closed 5 years ago.
I am using Http Basic Auth for a REST API where the username and password is sent in the header.This is the relevant config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and()
.httpBasic();
}
A controller method
#RestController
public class ScheduleController {
#Autowired
ScheduleService scheduleService;
#RequestMapping(value ="api/schedule/{scheduleId}",method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<Schedule> getOneSchedule(#PathVariable Long scheduleId) {
// Get the product given by Id
Schedule schedule = scheduleService.findOne(scheduleId);
if(schedule == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
return ResponseEntity.status(HttpStatus.OK).body(schedule);
}
}
Is it possible to inside the "getOneSchedule" method to obtain the User object for the username/password provided?
Yes,
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
and to fetch the username, for example just use authentication.getName()

Resources