Spring Boot Sping Boot webflux安全性与JWT令牌

68de4m5k  于 2024-01-06  发布在  Spring
关注(0)|答案(1)|浏览(167)

尝试使用Sping Boot webflux设置基于JWT token的身份验证。

Sping Boot 版本:- 2.3.0.BUild-SNAPSHOT
**技术栈:-**Angular 9、Sping Boot 2.3.0、BUILD-SNAPSHOT、Spring security、Spring security JWT

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-webflux</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>org.springframework.security</groupId>
  11. <artifactId>spring-security-jwt</artifactId>
  12. <version>1.1.0.RELEASE</version>
  13. </dependency>

字符串
GUI是基于angular 9的,并使用基于表单的身份验证。
需要JWT来调用来自Angular 和调用也来直接到API。

WebSecurityConfig,

  1. @Configuration
  2. @EnableWebFluxSecurity
  3. @EnableGlobalMethodSecurity(prePostEnabled = true)
  4. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  5. @Autowired
  6. private AuthenticationManager authenticationManager;
  7. @Autowired
  8. private ServerSecurityContextRepository securityContextRepository;
  9. @Bean
  10. public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
  11. return http.exceptionHandling().authenticationEntryPoint((swe, e) -> {
  12. return Mono.fromRunnable(() -> {
  13. swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
  14. });
  15. }).accessDeniedHandler((swe, e) -> {
  16. return Mono.fromRunnable(() -> {
  17. swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
  18. });
  19. }).and().csrf().disable().formLogin().disable().httpBasic().disable()
  20. .authenticationManager(authenticationManager).securityContextRepository(securityContextRepository)
  21. .authorizeExchange().pathMatchers(HttpMethod.OPTIONS).permitAll().pathMatchers("/login").permitAll()
  22. .anyExchange().authenticated().and().build();
  23. }
  24. @Bean
  25. public PBKDF2Encoder passwordEncoder() {
  26. return new PBKDF2Encoder();
  27. }
  28. }

PBKDF2Encoder,

  1. @Component
  2. public class PBKDF2Encoder implements PasswordEncoder {
  3. @Value("${springbootwebfluxjjwt.password.encoder.secret}")
  4. private String secret;
  5. @Value("${springbootwebfluxjjwt.password.encoder.iteration}")
  6. private Integer iteration;
  7. @Value("${springbootwebfluxjjwt.password.encoder.keylength}")
  8. private Integer keylength;
  9. /**
  10. * More info (https://www.owasp.org/index.php/Hashing_Java)
  11. *
  12. * @param cs password
  13. * @return encoded password
  14. */
  15. @Override
  16. public String encode(CharSequence cs) {
  17. try {
  18. byte[] result = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512")
  19. .generateSecret(
  20. new PBEKeySpec(cs.toString().toCharArray(), secret.getBytes(), iteration, keylength))
  21. .getEncoded();
  22. return Base64.getEncoder().encodeToString(result);
  23. } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {
  24. throw new RuntimeException(ex);
  25. }
  26. }
  27. @Override
  28. public boolean matches(CharSequence cs, String string) {
  29. return encode(cs).equals(string);
  30. }
  31. }

AuthenticationManager,

  1. @Component
  2. public class AuthenticationManager implements ReactiveAuthenticationManager {
  3. @Autowired
  4. private JWTUtil jwtUtil;
  5. @Override
  6. public Mono<Authentication> authenticate(Authentication authentication) {
  7. String authToken = authentication.getCredentials().toString();
  8. String username;
  9. try {
  10. username = jwtUtil.getUsernameFromToken(authToken);
  11. } catch (Exception e) {
  12. username = null;
  13. }
  14. if (username != null && jwtUtil.validateToken(authToken)) {
  15. Claims claims = jwtUtil.getAllClaimsFromToken(authToken);
  16. List<String> rolesMap = claims.get("role", List.class);
  17. List<Role> roles = new ArrayList<>();
  18. for (String rolemap : rolesMap) {
  19. roles.add(Role.valueOf(rolemap));
  20. }
  21. UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
  22. username,
  23. null,
  24. roles.stream().map(authority -> new SimpleGrantedAuthority(authority.name())).collect(Collectors.toList())
  25. );
  26. return Mono.just(auth);
  27. } else {
  28. return Mono.empty();
  29. }
  30. }
  31. }

SecurityContextRepository,

  1. @Component
  2. public class SecurityContextRepository implements ServerSecurityContextRepository {
  3. @Autowired
  4. private AuthenticationManager authenticationManager;
  5. @Override
  6. public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
  7. throw new UnsupportedOperationException("Not supported yet.");
  8. }
  9. @Override
  10. public Mono<SecurityContext> load(ServerWebExchange swe) {
  11. ServerHttpRequest request = swe.getRequest();
  12. String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
  13. if (authHeader != null && authHeader.startsWith("Bearer ")) {
  14. String authToken = authHeader.substring(7);
  15. Authentication auth = new UsernamePasswordAuthenticationToken(authToken, authToken);
  16. return this.authenticationManager.authenticate(auth).map((authentication) -> {
  17. return new SecurityContextImpl(authentication);
  18. });
  19. } else {
  20. return Mono.empty();
  21. }
  22. }
  23. }


这是正确的做法吗?还有更好的做法吗?

3b6akqbq

3b6akqbq1#

我也遇到过同样的问题,经过大量的研究,我做了一个功能完全100%的演示项目,实现了webflux + webflux-security +其他.
该实施包括:

  • Spring webflux
  • Spring安全性通过JWT +验证层实现
  • 用户注册演示端点
  • 用户身份验证端点
  • 模型到dto的Map(使用mapstruct)
  • User R2db with Postgresql repository impl
  • spring安全层中的用户验证,根据db中的用户记录

相关问题