spring-security JWT(Java):HTTP请求不考虑我的令牌中的角色,并返回错误403

hrysbysz  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(143)

我正在编写一个后端/APIRest,它是个人应用程序的一部分。我正在尝试通过JWT设置一个身份验证,角色为:ADMIN和USER。因为我只想为角色ADMIN筛选以下端点:/users/all但当我发送我的http请求从失眠应用程序,与localhost:7777/users/all我有一个错误403禁止.我不知道为什么.
这是一个由我发送的请求生成的JWT,你可以在jwt.io上查看它:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJrZW50MSIsInJvbGVzIjoiQURNSU4iLCJleHAiOjE2NjExNTUxNDUyOTUsImlhdCI6MTY2MDA3NTE0NTI5NX0.RGJzJVkM6bB0g6YlK6FFMzbjjTZ8qPqGf9pfHMeKHfyDV_OM9lF1w8SDzys3SC-iNdBMgXMjVP972URcISwJ_A

{1}/{2}相关终点控制器{3}/{4}

@RestController
@RequestMapping("/users")
public class AppUserController {

    @RolesAllowed("ADMIN")
    @GetMapping("/all")
    public ResponseEntity<List<AppUserListDto>> getAllAppUsers() {
        logger.info("GET /users/all");
            return new ResponseEntity<List<AppUserListDto>>(appUserServiceImpl.getAllUsers(), HttpStatus.OK);
        }

    }

/* 安全配置 */

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(
        prePostEnabled = true,
        securedEnabled = true,
        jsr250Enabled = true)
public class SecurityConfiguration {

    @Autowired
    RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    JwtFilter jwtFilter;    

    @Bean
    SecurityFilterChain web(HttpSecurity http) throws Exception {
        http.csrf().disable().exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers("/users/add").permitAll()
                .antMatchers("/users/all").hasRole("ADMIN")
                .anyRequest().authenticated();

        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        String hierarchy = "ADMIN > USER";
        roleHierarchy.setHierarchy(hierarchy);
        return roleHierarchy;
    }
    @Bean
    public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {
        DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
        expressionHandler.setRoleHierarchy(roleHierarchy());
        return expressionHandler;
    }

}

/* 我的用户详细信息服务 */

@Service
public class MyUserDetailService implements UserDetailsService  {

    @Autowired
    AppUserRepository appUserRepository;

    @Autowired
    RoleRepository roleRepository;

    @Override
    public UserDetails loadUserByUsername(String appUsername) throws UsernameNotFoundException {
        AppUserEntity appUser = appUserRepository.findByAppUsername(appUsername);
        if (appUser == null) {
            return new org.springframework.security.core.userdetails.User(
              " ", " ", true, true, true, true, 
              getAuthorities(Arrays.asList(
                roleRepository.findByRoleName("USER"))));
        }
        return new org.springframework.security.core.userdetails.User(
            appUser.getAppUsername(), appUser.getPassword(), true, true, true, 
            true, getAuthorities(appUser.getRoles()));
    }

    private Collection<? extends GrantedAuthority> getAuthorities(
            Collection<Role> roles) {

        return getGrantedAuthorities(getPrivileges(roles));
    }

    private List<String> getPrivileges(Collection<Role> roles) {

        List<String> privileges = new ArrayList<>();
        List<Privilege> collection = new ArrayList<>();
        for (Role role : roles) {
            privileges.add(role.getRoleName());
            collection.addAll(role.getPrivileges());
        }
        for (Privilege item : collection) {
            privileges.add(item.getName());
        }
        return privileges;
    }

    private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String privilege : privileges) {
            authorities.add(new SimpleGrantedAuthority(privilege));
        }
        return authorities;
    }
}

/* JWT 实用程序 */

@Component
public class JwtUtils {

    @Autowired
    AppUserRepository appUserRepository;

    long JWT_VALIDITY = 5 * 60 * 60 * 60;

    @Value("${jwt.secret}")
    String secret;

    private final Logger logger = LoggerFactory.getLogger(JwtUtils.class);

    public String generateToken(Authentication authentication) {

        Map<String, Object> claims = new HashMap<>();
        claims.put("roles",authentication.getAuthorities().stream().map(role -> role.getAuthority()).findFirst().orElseThrow(NoSuchElementException::new));
        claims.put("iat",new Date(System.currentTimeMillis()));
        claims.put("exp", new Date(System.currentTimeMillis() + JWT_VALIDITY * 1000));
        claims.put("sub", authentication.getName());

        Map<String, Object> headerJwt = new HashMap<>();
        headerJwt.put("alg", "HS512");
        headerJwt.put("typ", "JWT");

        //TODO Later : Header, claims, jwt... will be Base64urlEncoded
        return Jwts.builder()
                .setHeader(headerJwt)
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    public Authentication getAuthentication(String token) {
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        AppUserEntity appUser = appUserRepository.findByAppUsername(claims.getSubject());
        logger.info("THIS IS THE SUBJECT FROM CLAIMS : {}", claims.getSubject());

        Collection<? extends GrantedAuthority> authorities = getAuthorities(appUser.getRoles());

        User principal = new User(claims.getSubject(), "", authorities);

        return new UsernamePasswordAuthenticationToken(principal, token, authorities);
    }

    private Collection<? extends GrantedAuthority> getAuthorities(
            Collection<Role> roles) {

        return getGrantedAuthorities(getPrivileges(roles));
    }

    private List<String> getPrivileges(Collection<Role> roles) {

        List<String> privileges = new ArrayList<>();
        List<Privilege> collection = new ArrayList<>();
        for (Role role : roles) {
            privileges.add(role.getRoleName());
            collection.addAll(role.getPrivileges());
        }
        for (Privilege item : collection) {
            privileges.add(item.getName());
        }
        return privileges;
    }

    private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String privilege : privileges) {
            authorities.add(new SimpleGrantedAuthority(privilege));
        }
        return authorities;
    }

}

/*JWT控制器 */

@RestController
public class JwtController {

    @Autowired
    JwtUtils jwtUtils;

    @Autowired
    AuthenticationManagerBuilder authenticationManagerBuilder;

    @PostMapping("/login")
    public ResponseEntity<?> createAuthToken(@RequestBody JwtRequest jwtRequest) {
        Authentication authentication = logUser(jwtRequest.getAppUsername(), jwtRequest.getPassword());
        String jwt = jwtUtils.generateToken(authentication);
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add(AUTHORIZATION_HEADER, "Bearer " + jwt);
        Object principal = authentication.getPrincipal();
         return new ResponseEntity<>(new JwtResponse(((User) principal).getUsername()), httpHeaders, HttpStatus.OK);
    }

    public Authentication logUser(String appUsername, String password) {
        Authentication authentication = authenticationManagerBuilder.getObject()
                .authenticate(new UsernamePasswordAuthenticationToken(appUsername, password));

        SecurityContextHolder.getContext().setAuthentication(authentication);
        return authentication;
    }
}

/*JWT筛选器 */

@Component
public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    JwtUtils jwtUtils;

    public static final String AUTHORIZATION_HEADER = "Authorization";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String jwt = resolveToken(request);
        if (StringUtils.hasText(jwt)) {
            Authentication authentication = jwtUtils.getAuthentication(jwt);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        chain.doFilter(request, response);
    }

    private String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}
r7knjye2

r7knjye21#

解码后的令牌如下所示:

{
  "sub": "kent1",
  "roles": "ADMIN",
  "exp": 1661155145295,
  "iat": 1660075145295
}

据我所知,问题出在用户角色上。您的角色应该是这样的格式:

ROLE_ADMIN, or ROLE_CUSTOMER, etc.. (notice prefix ROLE_)

在设置了@RolesAllowed("ADMIN")注解的情况下,此零件:

.antMatchers("/users/all").hasRole("ADMIN")

则不需要。
顺便说一句,在您的JwtUtils类中,我注意到了下面这行代码:

claims.put("roles",authentication.getAuthorities());

通过这行代码,您可以设置

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

列表作为map中“roles”键的值。由于该列表将包含整个GrantedAuthority对象,因此这不是Spring所期望的。假设您的role表如下所示:

+---------+------------+
| id_role | role_name  |
+---------+------------+
|       1 | ROLE_ADMIN |
|       2 | ROLE_USER  |
+---------+------------+

您可以执行以下操作:

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        List<String> roles = new ArrayList<>();
        for(GrantedAuthority authority: authorities){
            if(authority.getAuthority().startsWith("ROLE")){
                roles.add(authority.getAuthority());
            }
        }
        claims.put("roles",roles);

这将解决我刚才描述问题。

相关问题