@PreAuthorize returns 403

@PreAuthorize returns 403

我在控制器中有以下方法

@GetMapping("/hello")
@PreAuthorize("hasRole('ADMIN')")
public String hello() {
    return "Hello " + JWTRequestFilter.UserClaim;
}

当具有 ADMIN 角色的用户尝试访问 /hello 时,返回 403。我在网络安全 class.

中启用了以下功能
@EnableGlobalMethodSecurity(prePostEnabled = true)

下面是 JWT 令牌。

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzZW50aHVyYW4iLCJSb2xlcyI6WyJBRE1JTiIsIlVTRVIiXSwiZXhwIjoxNTkzMDE0NDE5LCJpYXQiOjE1OTI5Nzg0MTl9.-7lTav3Nux8WVafUBGXjOxtXcE-r0fpfjb7wM7hrg6w

即使 JWT 令牌也有这个角色,但我仍然得到 403。这个预授权注释是否从 JWT 看到角色,或者它是否进行数据库调用并检查 user.Even 我有的角色使用 @PreAuthrize 注释但仍然得到相同的行为。如何解决这个403。下面我附上了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);
    }

}

这就是我生成 JWT 令牌以及设置角色的方式。

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());
}

确定问题的建议方法

        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);
        }

结果:令牌有效但权限未加载

Authorities granted : 

建议的解决方案 修复 MyUserDetailService 以在 userDetails

中加载 Authorities

Spring 为权限添加前缀 ROLE_。 您可以实现附加角色前缀的 setter。 或者另一种更简单的方法是有一个单独的类来实现 GrantedAuthority 接口

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,...