我可以注册一个用户,然后使用这些凭据登录,并收到一个包含userId、用户名和用户角色的有效JWT令牌。
这是一个具有两个角色(USER和ADMIN(的令牌的屏幕截图jwt.io结果
我手动在角色表中插入了两个角色(USER和ADMIN(,这样创建的每个用户在创建后都会自动分配一个ROLE_USER。
问题是,现在我想根据用户的角色访问URL,我在试图授予访问权限的端点上得到了401个未经授权的URL。
这是一个具有用户和管理员角色的用户的邮递员回复,但我仍然收到这个401。
邮递员响应
以下是重要的课程My Role实体有一个名称和描述字段My Repository接口有一个findByXXX方法
User.java
@Entity
@Table(name="user")
public class User implements UserDetails{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
@NotBlank(message = "Username is required")
private String username;
@NotBlank(message = "Password is required")
private String password;
@Transient
private String confirmPassword;
private boolean enabled = true;
@JsonFormat(pattern = "yyyy-mm-dd")
private Date createdAt;
// I want to load all the roles of users + the user itself once requested for
@ManyToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name="user_roles",
joinColumns = {
@JoinColumn(name="user_id")
},
inverseJoinColumns = {
@JoinColumn(name="role_id")
})
private Set<Role> roles = new HashSet<>();
// Constructor
// Getters and setteres
@Override
@JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
@JsonIgnore
public boolean isAccountNonExpired() {
return true;
}
@Override
@JsonIgnore
public boolean isAccountNonLocked() {
return true;
}
@Override
@JsonIgnore
public boolean isCredentialsNonExpired() {
return true;
}
}
SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
};
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/users/register", "/api/users/login", "/api/users/all").permitAll()
.antMatchers("/api/users/user/**").hasRole("USER")
.antMatchers("/api/users/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
// handles the json exception response if details are incorrect with an appropriate message
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
httpSecurity.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
UserService.java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
private RoleService roleService;
public User saveUser(User user) {
Role userRole = roleService.findByRoleName("USER");
// Role adminRole = roleService.findByRoleName("ADMIN");
Set<Role> roles = new HashSet<>();
try {
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setUsername(user.getUsername());
user.setConfirmPassword("");
roles.add(userRole);
user.setRoles(roles);
return userRepository.save(user);
} catch(Exception e) {
throw new UsernameAlreadyExistsException("User with username " + user.getUsername() + " already exists!");
}
}
public List<User> findAll() {
List<User> list = new ArrayList<>();
userRepository.findAll().iterator().forEachRemaining(list::add);
return list;
}
}
UserDetailsServiceImpl.java
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if(user == null) {
throw new UsernameNotFoundException("User not found");
}
return user;
}
private Set getAuthority(User user) {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
user.getRoles().forEach(role -> {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
});
return authorities;
}
}
JwtTokenProvider.java
@Component
public class JwtTokenProvider {
private final String SECRET = "SECRET";
// Generate the token
public String generateToken(Authentication authentication) {
User user = (User) authentication.getPrincipal();
Date now = new Date(System.currentTimeMillis());
Date expiryDate = new Date(now.getTime() + 300_000);
String userId = Long.toString(user.getId());
// this is what holds the token
// add roles in claims
Map<String, Object> claims = new HashMap<>();
claims.put("id", (Long.toString(user.getId())));
claims.put("username", user.getUsername());
claims.put("roles", user.getRoles());
return Jwts.builder().setSubject(userId).setClaims(claims).setIssuedAt(now).setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, SECRET).compact();
}
// Validate the token
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
return true;
} catch (SignatureException ex) {
System.out.println("Invalid JWT Signature");
} catch (MalformedJwtException ex) {
System.out.println("Invalid JWT Token");
} catch (ExpiredJwtException ex) {
System.out.println("Expired JWT Token");
} catch (UnsupportedJwtException ex) {
System.out.println("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
System.out.println("JWT claims string is empty");
}
return false;
}
public String getUsernameFromJWT(String token) {
Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
return claims.getSubject();
}
}
JwtAuthenticationFilter.java
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@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)) {
String username = tokenProvider.getUsernameFromJWT(jwt);
User userDetails = (User) userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, Collections.emptyList());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
private String getJWTFromRequest(HttpServletRequest request) {
// Header Authorization: Bearer token
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
UserController.java
@RestController
@RequestMapping("/api/users")
@CrossOrigin
public class UserController {
private UserService userService;
private UserDetailsServiceImpl userDetailsService;
private UserValidator userValidator;
private ErrorValidationService errorValidationService;
private JwtTokenProvider tokenProvider;
private AuthenticationManager authenticationManager;
@Autowired
public UserController(UserService userService, UserDetailsServiceImpl userDetailsService, UserValidator userValidator, ErrorValidationService errorValidationService, JwtTokenProvider tokenProvider, AuthenticationManager authenticationManager) {
this.userService = userService;
this.userDetailsService = userDetailsService;
this.userValidator = userValidator;
this.errorValidationService = errorValidationService;
this.tokenProvider = tokenProvider;
this.authenticationManager = authenticationManager;
}
// I want to allow role based access to these URLs
@GetMapping("/all")
public String welcomeAll() {
return "Anyone can view this!";
}
@GetMapping("/admin")
public String adminPing(){
return "Only Admins Can view This";
}
@GetMapping("/user")
public String userPing(){
return "Any User Can view This";
}
// Get all users
@GetMapping("/userList")
public ResponseEntity<?> getAllUsers() {
return ResponseEntity.ok(userService.findAll());
}
@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody User user, BindingResult result) {
userValidator.validate(user, result);
ResponseEntity<?> errorMap = errorValidationService.validationService(result);
if(errorMap != null) return errorMap;
User newUser = userService.saveUser(user);
return new ResponseEntity<User>(newUser, HttpStatus.CREATED);
}
@PostMapping("/login")
public ResponseEntity<?> login(@Valid @RequestBody LoginRequest loginRequest, BindingResult result) throws Exception {
System.out.println("Entering /login");
ResponseEntity<?> errorMap = errorValidationService.validationService(result);
if(errorMap != null) return errorMap;
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = "Bearer " + tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtLoginSuccessResponse(true, jwt));
}
}
如果有人能告诉我为什么会发生这种情况,以及我如何以更好的方式改变和抽象事物,我将不胜感激。由于网上有很多不同的实现,我不确定我做错了什么。
使用hasAnyAuthority
而不是hasRole
并从SimpleGrantedAuthority
中删除"ROLE_"
- 您的代码
.antMatchers("/api/users/user/**").hasRole("USER")
//...and...
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
- 试试这个
.antMatchers("/api/users/user/**").hasAnyAuthority("USER")
//...and...
authorities.add(new SimpleGrantedAuthority(role.getName()));
- 但我不知道你在哪里使用方法
UserDetailsServiceImpl.getAuthority
?您将authorities
的空列表放入JwtAuthenticationFilter
中的UsernamePasswordAuthenticationToken
中
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, Collections.emptyList());
你应该把authorities
放在.里