Spring-Boot Security 使用角色

huangapple go评论89阅读模式
英文:

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 = &quot;varchar(30)&quot;)
@Email
private String email;
@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;
@Column(nullable = false, columnDefinition = &quot;varchar(60)&quot;)
@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 = &quot;users_players&quot;,
joinColumns = {@JoinColumn(name = &quot;user_id&quot;, nullable = false)},
inverseJoinColumns = {@JoinColumn(name = &quot;player_id&quot;, nullable = false)}
)
@JsonIgnoreProperties({&quot;users&quot;})
private List&lt;Player&gt; players;
@Override
public Collection&lt;? extends GrantedAuthority&gt; 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 = &quot;b7acf8204b85ab7cec6e73b91f682677d642d328fdec7788e8cbff11ff1a5b22&quot;;
public String extractUsername(String jwtToken) {
return extractClaim(jwtToken, Claims::getSubject);
}
public &lt;T&gt; T extractClaim(String jwtToken, Function&lt;Claims, T&gt; claimsResolver) {
final Claims claims = extractAllclaims(jwtToken);
return claimsResolver.apply(claims);
}
public String generateJwtToken(Map&lt;String, Object&gt; 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&lt;&gt;(), userDetails);
}
public boolean isJwtTokenValid(String jwtToken, UserDetails userDetails) {
final String username = extractUsername(jwtToken);
return (username.equals(userDetails.getUsername())) &amp;&amp; !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(&quot;Authorization&quot;);
final String jwtToken;
final String userEmail;
if (authHeader == null || !authHeader.startsWith(&quot;Bearer &quot;)) {
filterChain.doFilter(request, response);
return;
}
jwtToken = authHeader.substring(7);
userEmail = jwtService.extractUsername(jwtToken);
if (userEmail != null &amp;&amp; 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 -&gt; csrf.disable())
.authorizeHttpRequests(auth -&gt; auth
.requestMatchers(&quot;/api/v1/auth/**&quot;).permitAll()
.anyRequest().authenticated()
)
.sessionManagement(sess -&gt; 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 -&gt; userRepository.findByEmail(username)
.orElseThrow(() -&gt; new UsernameNotFoundException(&quot;User not found&quot;));
}
@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(&quot;/register&quot;)
public ResponseEntity&lt;AuthenticationResponse&gt; registerUser(@RequestBody RegisterRequestDto request) {
return authenticationService.registerUser(request);
}
@PostMapping(&quot;/authenticate&quot;)
public ResponseEntity&lt;AuthenticationResponse&gt; authenticateUser(@RequestBody AuthRequestDto request) {
return authenticationService.authenticateUser(request);
}

}


The *DemoController* where i want the &quot;sayHelloAdmin&quot; method to be only accessed by an admin and the &quot;sayHelloUser&quot; method by user and admin:

@RestController
@RequestMapping("/api/v1/demo-controller")
public class DemoController {

@GetMapping(value = &quot;/admin&quot;)
public ResponseEntity&lt;String&gt; sayHelloAdmin() {
return ResponseEntity.ok(&quot;Hello from admin endpoint&quot;);
}
@GetMapping(value = &quot;/user&quot;)
public ResponseEntity&lt;String&gt; sayHelloUser() {
return ResponseEntity.ok(&quot;Hello from user endpoint&quot;);
}

}


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&lt;AuthenticationResponse&gt; 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&lt;&gt;());
userRepository.save(user);
var jwtToken = jwtService.generateJwtToken(user);
return new ResponseEntity&lt;&gt;(AuthenticationResponse.builder()
.jwtToken(jwtToken)
.build(), HttpStatus.CREATED);
}
@Override
public ResponseEntity&lt;AuthenticationResponse&gt; 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&lt;&gt;(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 -&gt; auth
.requestMatchers(&quot;/api/v1/auth/**&quot;).permitAll()
.requestMatchers(...).hasRole(&quot;USER&quot;)
.requestMatchers(...).hasAnyRole(&quot;USER&quot;, &quot;ADMIN&quot;)
.requestMatchers(...).hasAuthority(&quot;USER&quot;)
.requestMatchers(...).hasAnyAuthority(&quot;USER&quot;, &quot;ADMIN&quot;)
.anyRequest().authenticated()
)
英文:

You already have a fully working solution. Just extend a customizer

				.authorizeHttpRequests(auth -&gt; auth
.requestMatchers(&quot;/api/v1/auth/**&quot;).permitAll()
.requestMatchers(...).hasRole(&quot;USER&quot;)
.requestMatchers(...).hasAnyRole(&quot;USER&quot;, &quot;ADMIN&quot;)
.requestMatchers(...).hasAuthority(&quot;USER&quot;)
.requestMatchers(...).hasAnyAuthority(&quot;USER&quot;, &quot;ADMIN&quot;)
.anyRequest().authenticated()
)

huangapple
  • 本文由 发表于 2023年7月7日 04:36:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76632398.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定