Reputation: 1837
I have a following method in the controller
@GetMapping("/hello")
@PreAuthorize("hasRole('ADMIN')")
public String hello() {
return "Hello " + JWTRequestFilter.UserClaim;
}
When a user who has the ADMIN
role tries to access the /hello
, 403 is returned. I have enabled the following in the websecurity class.
@EnableGlobalMethodSecurity(prePostEnabled = true)
Below is the JWT token.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzZW50aHVyYW4iLCJSb2xlcyI6WyJBRE1JTiIsIlVTRVIiXSwiZXhwIjoxNTkzMDE0NDE5LCJpYXQiOjE1OTI5Nzg0MTl9.-7lTav3Nux8WVafUBGXjOxtXcE-r0fpfjb7wM7hrg6w
Even the JWT token has the role but still i'm getting 403. Does this preauthorize annotation see the role from the JWT or does it make a DB call and check the role of a user.Even I have used the @PreAuthrize
annotation but still getting the same behaviour. How to resolve this 403. Below I have attached the JWTRequestFilter
class.
public class JWTRequestFilter extends OncePerRequestFilter {
@Autowired
private MyUserDetailService userDetailService;
@Autowired
private JWTUtil jwtUtil;
public static String UserClaim = "";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")){
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
UserClaim = username;
}
if(username != null && SecurityContextHolder.getContext().getAuthentication() == null){
UserDetails userDetails= this.userDetailService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
This is how I'm generating the JWT token and how I set the roles.
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
Set<String> Userroles = new HashSet<>();
User user = userRepository.findByUsername(userDetails.getUsername());
for(Role role:user.getRoles()){
Userroles.add(role.getName());
}
claims.put("Roles",Userroles.toArray());
return createToken(claims, userDetails.getUsername());
}
Upvotes: 1
Views: 3439
Reputation: 176
Spring adds the prefix ROLE_ to the authorities. You can either implement a setter that appends the role prefix. Or another much simple way to do it would be to have a separate classe that implements GrantedAuthority interface
public class UserRole implements GrantedAuthority {
private MyRole role;
@Override
public String getAuthority() {
return "ROLE_" + role.toString();
}
}
//MyRole is the enum with the different roles ADMIN,VIEWER,...
Upvotes: 1
Reputation: 8203
Suggested Approach to identify the issue
if(username != null && SecurityContextHolder.getContext().getAuthentication() == null){
UserDetails userDetails= this.userDetailService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
String authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining());
System.out.println("Authorities granted : " + authorities);
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
} else {
System.out.println("Not Valid Token);
}
} else {
System.out.println("No Token);
}
Outcome: Token was valid but authorities were not loaded
Authorities granted :
Suggested Solution
Fix the MyUserDetailService
to load Authorities
in userDetails
Upvotes: 1