英文:
Spring-Boot Security use Roles
问题
以下是您提供的代码部分的翻译:
@Builder
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, columnDefinition = "varchar(30)")
@Email
private String email;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(nullable = false, columnDefinition = "varchar(60)")
@Length(min = 8, max = 256)
@NotBlank
private String password;
@Column
@Enumerated(EnumType.STRING)
private Role role;
@Column(nullable = false)
private String teamName;
@Column(nullable = false)
private int overallScore;
@Column(nullable = false)
private int gameDayScore;
@ManyToMany
@JoinTable(
name = "users_players",
joinColumns = {@JoinColumn(name = "user_id", nullable = false)},
inverseJoinColumns = {@JoinColumn(name = "player_id", nullable = false)}
)
@JsonIgnoreProperties({"users"})
private List<Player> players;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority(role.name()));
}
@Override
public String getUsername() {
return email;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
public enum Role {
USER,
ADMIN;
}
@Service
public class JwtService {
private static final String SECRET_KEY = "b7acf8204b85ab7cec6e73b91f682677d642d328fdec7788e8cbff11ff1a5b22";
public String extractUsername(String jwtToken) {
return extractClaim(jwtToken, Claims::getSubject);
}
public <T> T extractClaim(String jwtToken, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllclaims(jwtToken);
return claimsResolver.apply(claims);
}
public String generateJwtToken(Map<String, Object> extraClaims, UserDetails userDetails) {
return Jwts.builder()
.setClaims(extraClaims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public String generateJwtToken(UserDetails userDetails) {
return generateJwtToken(new HashMap<>(), userDetails);
}
public boolean isJwtTokenValid(String jwtToken, UserDetails userDetails) {
final String username = extractUsername(jwtToken);
return (username.equals(userDetails.getUsername())) && !isJwtTokenExpired(jwtToken);
}
private boolean isJwtTokenExpired(String jwtToken) {
return extractExpiration(jwtToken).before(new Date());
}
private Date extractExpiration(String jwtToken) {
return extractClaim(jwtToken, Claims::getExpiration);
}
private Claims extractAllclaims(String jwtToken) {
return Jwts
.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(jwtToken)
.getBody();
}
private Key getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(keyBytes);
}
}
@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
final String jwtToken;
final String userEmail;
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
jwtToken = authHeader.substring(7);
userEmail = jwtService.extractUsername(jwtToken);
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
if (jwtService.isJwtTokenValid(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
}
}
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
}
@Configuration
@RequiredArgsConstructor
public class ApplicationConfig {
private final UserRepository userRepository;
@Bean
public UserDetailsService userDetailsService() {
return username -> userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthenticationController {
private final AuthenticationService authenticationService;
@PostMapping("/register")
public ResponseEntity<AuthenticationResponse> registerUser(@RequestBody RegisterRequestDto request) {
return authenticationService.registerUser(request);
}
@PostMapping("/authenticate")
public ResponseEntity<AuthenticationResponse> authenticateUser(@RequestBody AuthRequestDto request) {
return authenticationService.authenticateUser(request);
}
}
@RestController
@RequestMapping("/api/v1/demo-controller")
public class DemoController {
@GetMapping(value = "/admin
<details>
<summary>英文:</summary>
I have recently looked into Spring-Boot and wanted to make a registration and login form for my application.
I have watched a video and build my application around, what he said in the video.
The authentication works and a jwt token is generated. When I have a valid token, i can send requests to methods without getting a 403 response.
My question: My User Class has a Role, which is an enum (USER, ADMIN)
How can I configure my application to check what role the current User has and bind this role to certain methods?
Here is my Code:
*User Entity*
@Builder
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, columnDefinition = "varchar(30)")
@Email
private String email;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(nullable = false, columnDefinition = "varchar(60)")
@Length(min = 8, max = 256)
@NotBlank
private String password;
@Column
@Enumerated(EnumType.STRING)
private Role role;
@Column(nullable = false)
private String teamName;
@Column(nullable = false)
private int overallScore;
@Column(nullable = false)
private int gameDayScore;
@ManyToMany
@JoinTable(
name = "users_players",
joinColumns = {@JoinColumn(name = "user_id", nullable = false)},
inverseJoinColumns = {@JoinColumn(name = "player_id", nullable = false)}
)
@JsonIgnoreProperties({"users"})
private List<Player> players;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority(role.name()));
}
@Override
public String getUsername() {
return email;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
*Role Enum Class*
public enum Role{
USER,
ADMIN;
}
*JwtService*
@Service
public class JwtService {
private static final String SECRET_KEY = "b7acf8204b85ab7cec6e73b91f682677d642d328fdec7788e8cbff11ff1a5b22";
public String extractUsername(String jwtToken) {
return extractClaim(jwtToken, Claims::getSubject);
}
public <T> T extractClaim(String jwtToken, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllclaims(jwtToken);
return claimsResolver.apply(claims);
}
public String generateJwtToken(Map<String, Object> extraClaims, UserDetails userDetails) {
return Jwts.builder()
.setClaims(extraClaims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public String generateJwtToken(UserDetails userDetails) {
return generateJwtToken(new HashMap<>(), userDetails);
}
public boolean isJwtTokenValid(String jwtToken, UserDetails userDetails) {
final String username = extractUsername(jwtToken);
return (username.equals(userDetails.getUsername())) && !isJwtTokenExpired(jwtToken);
}
private boolean isJwtTokenExpired(String jwtToken) {
return extractExpiration(jwtToken).before(new Date());
}
private Date extractExpiration(String jwtToken) {
return extractClaim(jwtToken, Claims::getExpiration);
}
private Claims extractAllclaims(String jwtToken) {
return Jwts
.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(jwtToken)
.getBody();
}
private Key getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(keyBytes);
}
}
*JwtAuthFilter*
@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
final String jwtToken;
final String userEmail;
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
jwtToken = authHeader.substring(7);
userEmail = jwtService.extractUsername(jwtToken);
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
if (jwtService.isJwtTokenValid(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
}
}
*SecurityConfig*
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
}
*ApplicationConfig*
@Configuration
@RequiredArgsConstructor
public class ApplicationConfig {
private final UserRepository userRepository;
@Bean
public UserDetailsService userDetailsService() {
return username -> userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
The *AuthenticationController*
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthenticationController {
private final AuthenticationService authenticationService;
@PostMapping("/register")
public ResponseEntity<AuthenticationResponse> registerUser(@RequestBody RegisterRequestDto request) {
return authenticationService.registerUser(request);
}
@PostMapping("/authenticate")
public ResponseEntity<AuthenticationResponse> authenticateUser(@RequestBody AuthRequestDto request) {
return authenticationService.authenticateUser(request);
}
}
The *DemoController* where i want the "sayHelloAdmin" method to be only accessed by an admin and the "sayHelloUser" method by user and admin:
@RestController
@RequestMapping("/api/v1/demo-controller")
public class DemoController {
@GetMapping(value = "/admin")
public ResponseEntity<String> sayHelloAdmin() {
return ResponseEntity.ok("Hello from admin endpoint");
}
@GetMapping(value = "/user")
public ResponseEntity<String> sayHelloUser() {
return ResponseEntity.ok("Hello from user endpoint");
}
}
and the *AuthenticationService*
@Service
@AllArgsConstructor
public class AuthenticationServiceImpl implements AuthenticationService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final JwtService jwtService;
private final AuthenticationManager authManager;
private final UserMapper userMapper;
@Override
public ResponseEntity<AuthenticationResponse> registerUser(RegisterRequestDto request) {
request.setPassword(passwordEncoder.encode(request.getPassword()));
User user = userMapper.registerRequestDtoToUser(request);
user.setRole(Role.USER);
user.setGameDayScore(0);
user.setOverallScore(0);
user.setPlayers(new ArrayList<>());
userRepository.save(user);
var jwtToken = jwtService.generateJwtToken(user);
return new ResponseEntity<>(AuthenticationResponse.builder()
.jwtToken(jwtToken)
.build(), HttpStatus.CREATED);
}
@Override
public ResponseEntity<AuthenticationResponse> authenticateUser(AuthRequestDto request) {
authManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword())
);
var user = userRepository.findByEmail(request.getEmail())
.orElseThrow();
var jwtToken = jwtService.generateJwtToken((user));
return new ResponseEntity<>(AuthenticationResponse.builder()
.jwtToken(jwtToken)
.build(), HttpStatus.OK);
}
}
*AuthRequestDto* and *RegisterRequestDto* are almost identical to the user entity but with fewer attributes.
Thanks in advance for your help!
sorry for the many codeblocks^^
cya!
~ Max
</details>
# 答案1
**得分**: 1
你已经有一个完全可用的解决方案。只需扩展一个自定义器:
```java
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers(...).hasRole("USER")
.requestMatchers(...).hasAnyRole("USER", "ADMIN")
.requestMatchers(...).hasAuthority("USER")
.requestMatchers(...).hasAnyAuthority("USER", "ADMIN")
.anyRequest().authenticated()
)
英文:
You already have a fully working solution. Just extend a customizer
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers(...).hasRole("USER")
.requestMatchers(...).hasAnyRole("USER", "ADMIN")
.requestMatchers(...).hasAuthority("USER")
.requestMatchers(...).hasAnyAuthority("USER", "ADMIN")
.anyRequest().authenticated()
)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论