spring security - 403 forbidden and not call loadUserByUsername() - spring

I added spring security to my app. I would like to secure any endpoints, and open "/users" for all
User configuration:
#Entity
#Table(name = "users")
#AllArgsConstructor
#NoArgsConstructor
public class UserEntity implements UserDetails {
#Id
private String id;
private String login;
private String password;
private String role;
public UserEntity(UserCreateRequestModel userCreateRequestModel) {
this.id = UUID.randomUUID().toString();
this.login = userCreateRequestModel.getLogin();
this.password = PasswordService.codePassword(userCreateRequestModel.getPassword());
this.role = "USER";
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority(role));
return new ArrayList<SimpleGrantedAuthority>();
}
#Override
public String getUsername() {
return login;
}
#Override
public String getPassword() {
return password;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
My custom WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
private final MyUserDetailsService myUserDetailsService;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/users", "/users/**").permitAll()
.anyRequest().hasAuthority("USER")
;
}
#Override
protected UserDetailsService userDetailsService() {
return myUserDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
and next I added custom UserDetailsService
#Service
#RequiredArgsConstructor
public class MyUserDetailsService implements UserDetailsService {
private final UserEntityRepository userEntityRepository;
#Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
System.out.println(userEntityRepository.findByLogin(login));
return userEntityRepository.findByLogin(login).orElseThrow((() -> new ObjectNotFoundException(login)));
}
and now I am getting problem. Endpoint for "/users" is open to all, I can send request with any problem. But when I am trying for example any #PostMapping on endpoint "/shapes"
I am getting 403 status response. In postman I think everything is OK:
of course user exists in database. In UserEntity user isEnabled, isAccountNonExpired, isAccountNonLocked and isCredentialsNonExpired - everything is on true.

In your getAuthorities() method of UserEntity class you're returning an empty List, so spring-security sees no authorities (roles) for an authenticated user.
Change this method like this:
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(new SimpleGrantedAuthority(role));
}

Related

why iam i getting 403 - forbidden

I was working on Spring Boot Security project and i did the authentication on some endpoints to prevent from access from USERS and granted the authority to ADMIN when ever i hit this URL : admin/users i got the login page i put the credentials and instead of returning the actual content its return type=Forbidden, status=403
here is my SecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// inject spring user details interface
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/api/**" , "/").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin();
}
#Bean
public BCryptPasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}
}
admin users controller
#RestController
#RequestMapping("admin/users")
public class UserController {
//
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
// get all users
#PreAuthorize("hasAnyRole('ADMIN')")
#GetMapping
public List<User> getAllUsers(){
return userRepository.findAll();
}
// test
#PreAuthorize("hasAnyRole('ADMIN')")
#GetMapping("/test")
public String message() {
return "secured end point";
}
//create new users
#PreAuthorize("hasAnyRole('ADMIN')")
#PostMapping
public User registerUser(#RequestBody User user){
String password = user.getPassword();
String encodedPassword = passwordEncoder.encode(password);
user.setPassword(encodedPassword);
return userRepository.save(user);
}
}
userDetail
public class CustomUserDetails implements UserDetails {
/**
*
*/
private static final long serialVersionUID = 1256711395932122675L;
private User user;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getRoles().stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
}
userDetailsService
[![#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
CustomUserDetail userDetail = null;
if(user != null) {
userDetail = new CustomUserDetail();
userDetail.setUser(user);
}
else {
throw new UsernameNotFoundException("user not exist with username " + username);
}
System.out.println("user details" + userDetail);
return userDetail;
}
}][2]][2]
I fixed the issue by simply correcting the getAuthorities() I removed "ROLE_" in simpeGrantedAuthority() , extracted the return into a variable in userDetail.java and used hasAuthority() instead of hasRole()
the code which was getting 403 - forbidden
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getRoles().stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
**the solution **
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = user.getRoles().stream().map(role ->
new SimpleGrantedAuthority(role.getRole()))
.collect(Collectors.toList());
return authorities;
}

Get Forbidden error in spring security role based authentication and authorization

I am trying to learn Spring security where I am facing a problem with Role based security.
There are 2 tables User and Role having One to Many realtions.
When I enter username and password in spring default form, I successfully fetch correct Users deatails through loadUserByUserName() method. But on screen I get a message like
This application has no explicit mapping for /error, so you are seeing
this as a fallback.
There was an unexpected error (type=Forbidden, status=403). Forbidden
Only #GetMapping("/user") method works properly.
Here is the controller part
#RestController
#RequestMapping("/admin")
public class AdminController {
#Autowired
UserRepository userRepo;
#Autowired
RoleRepository roleRepo;
#PreAuthorize("hasAnyRole('ADMIN')")
#PostMapping("/add")
public String addUserByAdmin(#RequestBody User user)
{
user.getRoles().forEach(role -> role.setUser(user));
userRepo.save(user);
return "User added Successfully";
}
#PreAuthorize("hasAnyRole('ADMIN')")
#GetMapping("/process")
public String process()
{
return "Processing....";
}
#GetMapping("/user")
public String users() // This code is working properly
{
System.out.println("U r in user area's");
return "User's space";
}
}
Configuration part
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
protected void configure(HttpSecurity http)throws Exception
{
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/admin/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin().permitAll();
}
}
Service part
#Service
public class CustomeUserDetailsService implements UserDetailsService
{
#Autowired
UserRepository userRepo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User u = userRepo.findByName(username);
CustomeUserDetails cUserDetails = new CustomeUserDetails();
if(u == null)
{
throw new UsernameNotFoundException("User "+username +"not found");
}
else
{
cUserDetails.setUser(u);
}
return cUserDetails;
}
}
Where am I wrong ?
What if I want to add one more URL which does not require any authnetication and authorization , how to add it ?
I think you should use "ROLE_ADMIN" instead of "ADMIN". The "ROLE_ADMIN" key can be saved in the database and turned into Collection. Below how I did.
CLASS THAT IMPLEMENTS USERDETAILS
public class UsuarioSS implements LdapUserDetails {
private static final long serialVersionUID = 1164806375870272028L;
private String cdusuariorede;
private Collection<GrantedAuthority> authorities;
public UsuarioSS() {
}
public UsuarioSS(String cdusuariorede,List<Perfil> perfis) {
super();
this.cdusuariorede = cdusuariorede;
this.authorities = new ArrayList<GrantedAuthority>();
for (Perfil perfil : perfis) {
this.authorities.add(new SimpleGrantedAuthority(perfil.toString()));
}
}
public String getId() {
return cdusuariorede;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return null;
}
#Override
public String getUsername() {
return cdusuariorede;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public boolean hasRole(String springSecurityKey) {
return getAuthorities().contains(new SimpleGrantedAuthority(springSecurityKey));
}
#Override
public void eraseCredentials() {
// TODO Auto-generated method stub
}
#Override
public String getDn() {
// TODO Auto-generated method stub
return null;
}
}
ENUM THAT REPRESENTS MY USER PROFILES (AUTHORITHYES)
public enum Perfil {
ROLE_ADMIN,ROLE_DEFAULT
}
A AUTHORIZATION FILTER
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private JWTUtil jwtUtil;
private UserDetailsService userDetailsService;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil,
UserDetailsService userDetailsService) {
super(authenticationManager);
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
UsernamePasswordAuthenticationToken auth = getAuthentication(header.substring(7));
if (auth != null) {
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(String token) {
if (jwtUtil.tokenValido(token)) {
String login = jwtUtil.getLogin(token);
UserDetails usuario = userDetailsService.loadUserByUsername(login);
return new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
}
return null;
}
}
MY CONTROLLER
#RestController
#RequestMapping(value = "/parte")
public class ParteController {
#Autowired
private ParteService service;
#PreAuthorize("hasAnyRole('ROLE_ADMIN')")
#GetMapping("/partes_demandadas_por_processo")
public ResponseEntity<List<TpPartesProcessoDto>> getPartesPorNuprocesso(
#RequestParam(name = "processo",required = true)
#Length(max = 15,min = 15,message = "O campo processo deve possuir 15 caracteres.")
String processo
) throws SQLException{
List<TpPartesProcessoDto> partes = service.getPartesdoProcessoPorNuprocesso(processo);
return ResponseEntity.ok().body(partes);
}
}

Unable to figureout on my own "[ UserDetailsService returned null, which is an interface contract violation ]"?

I am new to spring boot and spring security and not able to understand the following error on my own.
my spring boot application simply contains two URLs one which is accessed by anyone i.e, only by whose name password is saved in database and another which can only ADMIN can access(to add user and there roll in MySql database).
But when i am passing username and password it saying:-
org.springframework.security.authentication.InternalAuthenticationServiceException: UserDetailsService returned null, which is an interface contract violation
I am posting all necessary class below:-
CustomUserDetailService:-
#Service
public class CustomUserDetailService implements UserDetailsService {
#Autowired
private UserRepository repository;
#Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
User user= repository.findByname(name);
CustomUserDetail userDetail=null;
if(user!=null){
CustomUserDetail userDetails=new CustomUserDetail();
userDetails.setUser(user);
}else{
throw new UsernameNotFoundException("User not exist with name :" +name);
}
return null;
}
}
CustomUserDetail
public class CustomUserDetail implements UserDetails {
private User user;
/*Getter and Setter*/
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
/*Overriden methods from userDetail*/
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getRoles().stream().map(role -> new SimpleGrantedAuthority("Role_"+role))
.collect(Collectors.toList());
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getName();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
BasicConfig:-
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class BasicConfig extends WebSecurityConfigurerAdapter{
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder(encodePWD());
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/user/").permitAll()
.and()
.authorizeRequests()
.antMatchers("/MiniApi/**").hasAnyRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll();
}
#Bean
public BCryptPasswordEncoder encodePWD()
{
return new BCryptPasswordEncoder();
}
}
Controller Classes:-
I am not making two #Restcontroller classes and only single #RequestMapping() is acting as base URL
AdminController:-
#RestController
#RequestMapping("/MiniApi")
public class Admincontroller
{
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#PreAuthorize("hasAnyRole('ADMIN')")
#PostMapping("/admin/add")
public String addUser(#RequestBody User user)
{
String pwd = user.getPassword();
String encryptPwd = passwordEncoder.encode(pwd);
user.setPassword(encryptPwd);
userRepository.save(user);
return "User added successfully...";
}
}
AnyOne:-
public class AnyOne {
#GetMapping("/anyone")
public String Anyone()
{
return "processing......";
}
}
Change what i made:-
If i am removing return statement from CustomUserDetailService i am getting return statement missing error and then i added return userDetails;
it gives me :-
First it askes me for username and password i provided it and then this
HTTP Status 403 – Forbidden
You are returning null instead of userDetails
#Service
public class CustomUserDetailService implements UserDetailsService {
#Autowired
private UserRepository repository;
#Override
public UserDetails loadUserByUsername(String name) throws
UsernameNotFoundException {
User user= repository.findByname(name);
CustomUserDetail userDetail=null;
if(user != null){
CustomUserDetail userDetails=new CustomUserDetail();
userDetails.setUser(user);
return userDetails;
}
throw new
UsernameNotFoundException("User not exist with name :" +name);
}
}

Spring Security + MongoDB, request get 401 all the time

I am learning how to use Spring and how to implement Spring Security in my project, using Roles and MongoDB.
I have a User model with a role list, and I want to let only ADMIN user to use some endpints from Controller.
Here is the User class and Role enum:
#Document(collection = "Users")
public class User {
#Id
private String id;
private String firstName;
private String lastName;
private String email;
private String password;
private List<Role> roles;
//constructor + getters and setters
}
public enum Role {
ADMIN,
BASIC_USER
}
I use the this UserDetails implementation: I think here is the problem...
public class CustomUserDetails implements UserDetails {
private User user;
public CustomUserDetails() {
}
public CustomUserDetails(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.user.getRoles().stream().map(role -> new SimpleGrantedAuthority(String.format("ROLE_%s", role))).collect(Collectors.toList());
}
#Override
public String getPassword() {
return this.user.getPassword();
}
#Override
public String getUsername() {
return this.user.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public User getUser() {
return user;
}
}
The UserDetailsService looks like this:
public class CustomUserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
final User user = userRepository.findByEmail(email);
if (Objects.isNull(user)) {
throw new UsernameNotFoundException("User not found");
}
return new CustomUserDetails(user);
}
}
I made this configuration:
#Configuration
#EnableWebSecurity(debug = true)
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(createUserDetailsService()).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/users/**").permitAll()
.antMatchers( "/users/**").authenticated().anyRequest().hasAnyRole("ADMIN")
.antMatchers("/users/me").authenticated().anyRequest().hasAnyRole("ADMIN", "BASIC_USER")
.antMatchers( "/users/**").permitAll()
.and()
.formLogin().disable()
.httpBasic();
}
#Override
public void configure(WebSecurity web) throws Exception {
/* To allow Pre-flight [OPTIONS] request from browser */
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
/**
* Create password encoder bean used for encrypting the password
*
* #return
*/
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* Create user service bean used for find the user by email
* #return
*/
#Bean
public UserDetailsService createUserDetailsService() {
return new CustomUserDetailsServiceImpl();
}
}
When I make any call with Postman at localhost:8080/users using Basic auth with the admin details from DB, all the time I get 401 Unauthorized Status
I think the problem is that I use an Enum for Roles and I don't know how to correctly build UserDetails implementation.
If helps, this is the UserController
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserService userService;
#GetMapping(path = "")
public List<User> getUsers(){
return this.userService.getUsers();
}
}
When I used MySQL for this project (With the same classes for security) the apps worked perfectly. In that implementation I used Roles, Users and Users_Roles tables.

no bean for AuthenticationManagerBuilder

I was following some tutorial for basic authentication using OAuth2. Whole thing works, but tests no. There are even simple class unit tests not working. I've got:
No qualifying bean of type org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder'.
AuthorizationServerConfiguration:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private AuthenticationManager authenticationManager;
#Autowired
public AuthorizationServerConfig(AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder){
this.authenticationManager = authenticationManager;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("clientId")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT","ROLE_TRUSTED_CLIENT").scopes("read","write","trust")
.resourceIds("oauth2-resource").accessTokenValiditySeconds(3600).secret("secret");
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()");
}
}
UserDetails implementation:
public class CustomUserDetails implements UserDetails {
private static final long serialVersionUID = -3962786160780714576L;
private Collection<? extends GrantedAuthority> authorities;
private String password;
private String username;
private boolean enabled;
public CustomUserDetails(User user) {
this.username = user.getUsername();
this.password = user.getPassword();
this.authorities = translate(user.getRoles());
this.enabled = user.getEnabled();
}
private Collection<? extends GrantedAuthority> translate(List<Role> roles) {
List<GrantedAuthority> authorities = new ArrayList<>();
roles.stream().forEach(x -> authorities.add(new SimpleGrantedAuthority(x.getName())));
return authorities;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return enabled;
}
}
ResourceServerConfigurerAdapter:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable().and()
.authorizeRequests()
.antMatchers("/","/home","/register","/login").permitAll()
.antMatchers("/dev/hello-world").authenticated();
}
}
#SpringBootApplication
public class LimeApplication {
public static void main(String[] args) {
SpringApplication.run(LimeApplication.class, args);
}
Main class:
#SpringBootApplication
public class SomeApplication {
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class, args);
}
#Autowired
public void authenticationManager(AuthenticationManagerBuilder builder, IUserDAO dao, PasswordEncoder encoder) throws Exception {
builder.userDetailsService(userDetailsService(dao)).passwordEncoder(encoder);
}
private UserDetailsService userDetailsService(final IUserDAO dao) {
return username -> new CustomUserDetails(dao.findByUsername(username));
}
}
application.yml
security:
oauth2:
resource:
filter-order: 3
I was looking and I was trying to disable authentication in test/resources/application.yml by
security:
basic:
enabled: false
But this does not work. I cannot find any answer how to correct or mock this. Can anyone help me? Does anyone know how to mock it? Or get this bean in tests?
Edit:
Tests are running with #DataJpaTest #RunWith(SpringRunner.class) annotations

Resources