0

I've created a form that sends the data to my backend, which persists it into the database enter image description here

This works well as long as I have .permitAll() on my antMatcher, but when I try to secure it so that only admins can make that call (admin role in the DB is ROLE_ADMIN), it returns a 401 Unauthorized Access with no message. I've tried

  • .hasRole("ADMIN")
  • .hasRole("ROLE_ADMIN")
  • .hasAuthority("ADMIN")
  • .hasAuthority("ROLE_ADMIN")

None of them works. enter image description here

enter image description here

My request looks like this (posting for the headers):

enter image description here

My SecurityConfig class:

@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity( securedEnabled = true, jsr250Enabled = true, prePostEnabled = true ) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserDetailsServiceImpl userDetailsService; @Autowired private JwtAuthenticationEntryPoint unauthorizedHandler; @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Bean(BeanIds.AUTHENTICATION_MANAGER) @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http .cors() .and() .csrf() .disable() .exceptionHandling() .authenticationEntryPoint(unauthorizedHandler) .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/", "/favicon.ico", "/**/*.png", "/**/*.gif", "/**/*.svg", "/**/*.jpg", "/**/*.html", "/**/*.css", "/**/*.js") .permitAll() .antMatchers("/api/auth/**") .permitAll() .antMatchers("/api/book/**") .permitAll() .antMatchers("/api/author/**") // .permitAll() .hasAnyRole("ROLE_ADMIN", "ADMIN", "ROLE_USER", "USER", "ROLE_ROLE_ADMIN", "ROLE_ROLE_USER") .anyRequest() .authenticated(); http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } } 

My UserDetailsServiceImpl class:

@Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired UserRepository userRepository; @Override @Transactional public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { User user = userRepository.findByEmail(email); return UserDetailsImpl.create(user); } @Transactional public UserDetails loadUserById(Integer id) { User user = userRepository.findById(id).orElseThrow( () -> new UsernameNotFoundException("User not found with id: " + id) ); return UserDetailsImpl.create(user); } } 

My JwtAuthenticationEntryPoint class:

@Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class); @Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { logger.error("Unauthorized access. Message:", e.getMessage()); httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); } } 

My JwtAuthenticationFilter:

public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtTokenProvider tokenProvider; @Autowired private UserDetailsServiceImpl userDetailsService; private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class); @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { try { String jwt = getJwtFromRequest(httpServletRequest); if(StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) { Integer userId = tokenProvider.getUserIdFromJWT(jwt); UserDetails userDetails = userDetailsService.loadUserById(userId); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); } } catch (Exception e) { logger.error("Could not set user authentication in security context", e); } filterChain.doFilter(httpServletRequest, httpServletResponse); } private String getJwtFromRequest(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7, bearerToken.length()); } return null; } } 

The JWT Token's validity is properly checked. That's not the issue at hand. Any help is appreciated.

EDIT: Added implementation of UserDetailsImpl:

public class UserDetailsImpl implements UserDetails { private Integer id; @JsonIgnore private String email; private String name; @JsonIgnore private String password; private boolean isAdmin; private Collection<? extends GrantedAuthority> authorities; public UserDetailsImpl(Integer id, String email, String name, String password, boolean isAdmin, Collection<? extends GrantedAuthority> authorities) { this.id = id; this.name = name; this.email = email; this.password = password; this.authorities = authorities; this.isAdmin = isAdmin; } public static UserDetailsImpl create(User user) { List<GrantedAuthority> authorities = user.getRoles().stream().map(role -> new SimpleGrantedAuthority(role.getName().name()) ).collect(Collectors.toList()); boolean isAdmin = false; for(Role role: user.getRoles()) { if(RoleName.ROLE_ADMIN.equals(role.getName())) { isAdmin = true; } } return new UserDetailsImpl( user.getId(), user.getEmail(), user.getName(), user.getPassword(), isAdmin, authorities ); } public Integer getId() { return id; } public String getName() { return name; } @Override public String getUsername() { return email; } @Override public String getPassword() { return password; } public boolean isAdmin() { return isAdmin; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserDetailsImpl that = (UserDetailsImpl) o; return Objects.equals(id, that.id); } @Override public int hashCode() { return Objects.hash(id); } 

Added this to check if the authorities are present after UserDetailsImpl.create(user) call: enter image description here

Output:

enter image description here

Login part of the AuthenticationController:

enter image description here

11
  • Does your user have Admin ROLE? Commented Oct 19, 2020 at 13:43
  • 1
    Could you please share your Implementation of UserDetailsImpl.create() ? Maybe you're not granting the proper role and authorities at that moment Commented Oct 19, 2020 at 13:44
  • can you show us what the output of userDetails.getAuthorities() is? Commented Oct 19, 2020 at 13:59
  • @tashkhisi As far as I can tell, yes, it should have it. Commented Oct 19, 2020 at 14:28
  • @Youri I added at the end of the post the implementation and the output of .getauthorities after .create(user) Commented Oct 19, 2020 at 14:31

1 Answer 1

1

I see that you are not updating the SecurityContextHolder. Unable to put it in a comment, so I wrote it here.

authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)) SecurityContextHolder.getContext().setAuthentication(authentication); //this seems missing 
Sign up to request clarification or add additional context in comments.

1 Comment

Oh, it is missing in the JwtAuthenticationFilter class. I'll see if that does the trick.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.